diff --git a/.github/workflows/publish-cvcgl-wasm.yml b/.github/workflows/publish-cvcgl-wasm.yml index 052d643d1..b1d826d51 100644 --- a/.github/workflows/publish-cvcgl-wasm.yml +++ b/.github/workflows/publish-cvcgl-wasm.yml @@ -1,7 +1,7 @@ name: publish-cvcgl-wasm # Build + publish libcvc and cvcgl for the wasm-mt platform (WebAssembly + -# pthreads), so downstream wasm consumers (CVC-DBG/cvcdbg's demos) can +# pthreads), so downstream wasm consumers (browser apps built on cvcGL) can # `cvcpkg install cvc/cvcgl --platform wasm-mt` instead of rebuilding cvcGL from # source. The wasm-mt DEPS (vtk/imgui/boost/codecs, all wasm32) are already on # the catalog; only libcvc + cvcgl were missing. cvcgl is built WITH imgui, so @@ -88,9 +88,9 @@ jobs: done - name: Build + install libcvc + cvcGL (wasm-mt, with imgui + sdl3) - # The proven build from CVC-DBG/cvcdbg wasm/build-standalone.sh step 1: # ONE emscripten configure builds both cvc + cvcGL targets and installs - # them into $INST. -DCVC_BUILD_EXAMPLES=OFF: libcvc's own demos never build. + # them into $INST (the same single-configure shape a standalone downstream + # wasm-mt app build uses). -DCVC_BUILD_EXAMPLES=OFF: libcvc's own demos never build. run: | # shellcheck disable=SC1091 source "$CVC_EMSDK_DIR/emsdk_env.sh" diff --git a/CMake/cvcConfig.cmake.in b/CMake/cvcConfig.cmake.in index 20511289a..011233619 100644 --- a/CMake/cvcConfig.cmake.in +++ b/CMake/cvcConfig.cmake.in @@ -126,7 +126,7 @@ if(@CVC_ENABLE_IMAGEMAGICK@) # find_library, and it wins on a wasm consumer (it finds the static libs in the prefix # without pkg-config). Gating on _cvc_im_synth skipped the append for that stock target, # so a wasm consumer's link failed with undefined BZ2_*/lzma_*/xml* symbols — which is - # what broke the cvcdbg demo3 gallery even after the export was made relocatable. Any + # what kept a downstream wasm app from linking even after the export was made relocatable. Any # MagickCore that reaches here is either a static one that NEEDS the closure or a shared # one for which the append is a harmless dedup, so run it whenever the target exists. if(TARGET ImageMagick::MagickCore) diff --git a/README.md b/README.md index b8992e1cc..bafa5e9c1 100644 --- a/README.md +++ b/README.md @@ -505,7 +505,7 @@ Measured 3.7–6.0× fewer triangles on the Austin bundle; `solve()` for a - **[docs/CUDA_GUIDE.md](docs/CUDA_GUIDE.md)** - CUDA usage guide - **[docs/NAV_TRAINING.md](docs/NAV_TRAINING.md)** - `cvc::nav` self-supervised policy training (torch-free, CPU + CUDA; surrogate vs bicycle rollout) - **[docs/NAV_VEHICLE.md](docs/NAV_VEHICLE.md)** - `cvc::nav` optional vehicle refinements: multi-disc footprint (+ the `body_gain` correction it needs), inner-wheel steering lock, and a material grip field -- **[docs/NAV_STATS.md](docs/NAV_STATS.md)** - `cvc::nav::nav_stats` base navigation telemetry: the per-vehicle/per-episode collector + corpus scorecard, the `sim_world` internal collector, and the `min_clearance_world()` units contract (RF-free base of the two-layer nav-stats design) +- **[docs/NAV_STATS.md](docs/NAV_STATS.md)** - `cvc::nav::nav_stats` base navigation telemetry: the per-vehicle/per-episode collector + corpus scorecard, the `sim_world` internal collector, and the `min_clearance_world()` units contract (domain-neutral base of the two-layer nav-stats design) - **[docs/LOD_API.md](docs/LOD_API.md)** - `cvc::lod` level-of-detail selection math: rung selection, budget solver, presets, and the user-facing knobs - **[docs/STREAMING.md](docs/STREAMING.md)** - `cvc::ariadne::stream` real-time frame transport: zero-copy video/audio/sensor streams, the host interface (DSL `stream-open`/`gl-bind-stream`, the `/streams/` descriptor, §4.1 scoping), GL/pycvc sinks, and the peripheral `frame_source` model (SDL3 camera + the composable audio-source family) with examples diff --git a/bindings/pycvc/pymod_gl/scenes.py b/bindings/pycvc/pymod_gl/scenes.py index 691b400ce..4ab19a2aa 100644 --- a/bindings/pycvc/pymod_gl/scenes.py +++ b/bindings/pycvc/pymod_gl/scenes.py @@ -2,8 +2,9 @@ a :class:`pycvc_gl.lab.Lab`. These are generic loaders for a ``geometry_bundle`` export — a ``terrain.json`` -heightfield plus a ``buildings.glb`` (glTF 2.0) city mesh, as produced by the -CVC-DBG ``geometry-scene-gen`` tool (e.g. the Austin bundle). The glTF is loaded +heightfield plus a ``buildings.glb`` (glTF 2.0) city mesh, as produced by an +OpenStreetMap + SRTM scene generator (e.g. the Austin scene bundle the nav demos +load, ``scene-austin-south-small`` on cvcpkg). The glTF is loaded NATIVELY via libcvc (``pycvc.load_model`` → ``cvc::model``, the Assimp-backed mesh loader — no ``vtkGLTFReader``, no trimesh/pygltflib) and added as a single native geometry node; the terrain becomes a draped surface mesh; a bilinear @@ -29,7 +30,7 @@ def terrain_grid(path: str): ``bounds2d`` = ``(min_x, min_y, max_x, max_y)``. The stored grid is TOP-DOWN (row 0 = north = ``max_y``, the raster/SRTM/GeoTIFF - convention the geometry-scene-gen tool inherits). Our mesh + sampler use the + convention the scene generator inherits). Our mesh + sampler use the opposite, bottom-up convention (row 0 -> ``min_y``, so a rising world ``y`` walks up the row index). We normalize here by reversing the rows so BOTH the terrain mesh and the drape sampler agree with the glTF's world frame — without diff --git a/cvcpkg/recipes/cvcgl/recipe.yaml b/cvcpkg/recipes/cvcgl/recipe.yaml index 310aaa3dd..7e17eb38a 100644 --- a/cvcpkg/recipes/cvcgl/recipe.yaml +++ b/cvcpkg/recipes/cvcgl/recipe.yaml @@ -23,7 +23,7 @@ recipe: # so cvc::gl::ImGuiOverlay actually renders for downstream GUI consumers instead of # being an inert stub. imgui is now a runtime dep (the export links the relocatable # $). Prior bundles shipped imgui-less, forcing consumers - # (cvcdbg demos) to rebuild cvcGL from source. + # that draw an overlay to rebuild cvcGL from source. # Bumped 2 -> 3: republish cvcGL against libcvc 3.4.0+cvc.3. The published +cvc.2 was built # against libcvc +cvc.2 and leaks an EXACT pin to it (cvcpkg records the built-against dep # version); that dragged the pre-guard libcvc +cvc.2 into wasm-mt consumer installs, so @@ -40,7 +40,7 @@ recipe: # Bumped 5 -> 6: rebuild against the relocatable-export libcvc +cvc.5 (libcvc #453). cvcGL source # is unchanged, but cvcgl exact-pins the libcvc it built against — the published +cvc.5 pins the # broken-export libcvc +cvc.4, so `cvcpkg install cvc/libcvc cvc/cvcgl --platform wasm-mt` dragged - # +cvc.4 back in and the demo3 wasm link still failed. This reships cvcgl +cvc.6 built against (and + # +cvc.4 back in and a consumer's wasm link still failed. This reships cvcgl +cvc.6 built against (and # re-pinning) libcvc +cvc.5, so the wasm consumer install stays on the clean export end-to-end. # Bumped 6 -> 7: rebuild against + re-pin libcvc +cvc.6 (the consumer-side ImageMagick # delegate-closure fix in cvcConfig.cmake.in). cvcGL source unchanged; the exact-pin means diff --git a/cvcpkg/recipes/libcvc/recipe.yaml b/cvcpkg/recipes/libcvc/recipe.yaml index 41bb24235..c580d2e92 100644 --- a/cvcpkg/recipes/libcvc/recipe.yaml +++ b/cvcpkg/recipes/libcvc/recipe.yaml @@ -72,8 +72,8 @@ recipe: # Bumped 1 -> 2 (3.4.0 lineage): republish the family so the bundle ships the # nav::sim_world::set_material_lam live setter (#433). It is an inline header # method (no exported symbol / ABI change), but consumers that compile against - # the SDK headers need it present -- notably the cvcdbg demo3 force-bias slider - # (CVC-DBG/cvcdbg#142), whose wasm-mt build installs cvc/libcvc from the catalog + # the SDK headers need it present -- notably a downstream wasm-mt app's live + # force-bias control, whose build installs cvc/libcvc from the catalog # and will not compile until 3.4.0+cvc.2 (with the setter) is published. The # published 3.4.0+cvc.1 predates #433. cvcgl already sits at +cvc.2, so this # brings the tested-together family back to a single revision. @@ -81,8 +81,8 @@ recipe: # (two-head sigmoid λ_soft/λ_hard, #436). The wasm-mt republish also (a) turns CVC_STATE_EXEC back # ON for wasm (it was wrongly forced OFF — the state_exec evaluator has no gRPC dependency and must # always build), and (b) picks up the gRPC/xmlrpc dep guard above so `cvcpkg install cvc/libcvc - # --platform wasm-mt` resolves again. A v2 .cvcnav hard-fails on a pre-v2 host, so hosts (demo3 / - # cvcdbg fleet) must land 3.4.0+cvc.3 before any v2 blob ships. + # --platform wasm-mt` resolves again. A v2 .cvcnav hard-fails on a pre-v2 host, so every host that + # loads .cvcnav blobs must land 3.4.0+cvc.3 before any v2 blob ships. # Bumped 3 -> 4 (3.4.0 lineage): the +cvc.3 comment above was WRONG — wasm-mt did NOT resolve. The # +cvc.3 gRPC/xmlrpc guard only scoped abseil/protobuf/grpc; `pack` bakes a bundle's required_deps # from depends.runtime FILTERED BY the target platform (builder.py generate_manifest), so the wasm-mt @@ -98,8 +98,8 @@ recipe: # Bumped 4 -> 5 (3.4.0 lineage): the +cvc.4 wasm-mt bundle resolved cleanly but its EXPORTED # cvcTargets.cmake baked 15 build-machine absolute paths into cvc::cvc's INTERFACE_LINK_LIBRARIES # (libMagick++/libMagickCore + the codec delegates + libzstd, all under /home/runner/.../wasm-deps), - # so a relocated consumer's link died with "libMagick++-7.Q16HDRI.a ... missing" — which is why the - # cvcdbg wasm demo3 gallery could never link. libcvc #453 makes the STATIC wasm export relocatable + # so a relocated consumer's link died with "libMagick++-7.Q16HDRI.a ... missing" — which is why a + # downstream wasm app could never link. libcvc #453 makes the STATIC wasm export relocatable # (synthesise ImageMagick::Magick++/::MagickCore + PkgConfig::ZSTD imported targets so the export # names targets, not paths; cvcConfig.cmake.in re-resolves them per-consumer). A recipe edit can't # fix the already-published +cvc.4 bundle — this reships a clean +cvc.5 wasm-mt export. Native is diff --git a/docs/NAV_MATERIAL.md b/docs/NAV_MATERIAL.md index faf6f715a..347f93768 100644 --- a/docs/NAV_MATERIAL.md +++ b/docs/NAV_MATERIAL.md @@ -160,8 +160,8 @@ reconstructs the exact map. A v2 blob **hard-fails on a pre-v2 host** (the loade requires format v2 for `kFlagLamSigmoid`); the whole round-trip — widen to two heads, serialize, reload, drive — is pinned by `nav_material_deploy_test`. -To steer on the learned grip/risk policy **while** an external force (e.g. -cvc::dbg's RF/comm push) also acts, use the fused `drive_step_material_ext` / +To steer on the learned grip/risk policy **while** an external force (e.g. a +downstream layer's own steering push) also acts, use the fused `drive_step_material_ext` / `bicycle_rollout_material_ext` — `sim_world::step()` selects it automatically when both a material stack and an `ext_force` are attached (otherwise material would take priority and drop the ext force). The whole round-trip — a widened net @@ -173,10 +173,9 @@ learned-lam reroute) — is pinned by `nav_material_deploy_test`. > *means*. The two-head material soft/hard channel documented here is the > reusable template — a precomputed risk/hazard field driving `−λ_s∇r̃ − > λ_h b′(φ)∇φ`. A downstream layer can re-source that template from a different -> field entirely and inject it through the same seam without any change here; the -> RF/comm re-sourcing (the `cvc::dbg` layer) is exactly that, and its +> field entirely and inject it through the same seam without any change here; its > domain-specific logic stays in that layer, never in this core (as with the -> RF-free base statistics — see `NAV_STATS.md`). +> domain-neutral base statistics and their extension seam — see `NAV_STATS.md`). ### Choosing constants diff --git a/docs/NAV_STATS.md b/docs/NAV_STATS.md index 42ad90985..29c0d25fd 100644 --- a/docs/NAV_STATS.md +++ b/docs/NAV_STATS.md @@ -1,15 +1,18 @@ # cvc::nav navigation statistics -`cvc::nav::nav_stats` (`inc/cvc/nav/nav_stats.h`) is the **base, RF-free** navigation-telemetry -layer: per-vehicle + per-episode motion/clearance/collision/material/time/fuel/budget stats over a -`sim_world` drive, plus a corpus scorecard for ranking training checkpoints. It is the shared base of -the two-layer nav-stats design (the RF/comms extension lives in `cvc::dbg`, in the CVC-DBG/cvcdbg -repo, and joins this record by `veh_index`; it never lives here). Field names mirror the Python -`grl_snam.scorecard` schema, and the two are held to the same hand-computed numbers by parity tests. +`cvc::nav::nav_stats` (`inc/cvc/nav/nav_stats.h`) is the **base, domain-neutral** +navigation-telemetry layer: per-vehicle + per-episode motion/clearance/collision/material/time/fuel/ +budget stats over a `sim_world` drive, plus a corpus scorecard for ranking training checkpoints. It is +the open core of a two-layer design. Everything here is generic navigation; the second layer is an +**extension seam**. A downstream layer that needs domain-specific metrics (for example, about an +external force it injects through `ext_force`) keeps its own per-vehicle record, joins it to this one +by `veh_index`, and nests it in the JSON itself. Nothing domain-specific lives here. Field names +mirror the Python `grl_snam.scorecard` schema, and the two are held to the same hand-computed numbers +by parity tests. The collector is stdlib-only (no libcvc-internal dependency beyond the STL): it consumes the -`sim_world::snapshot` arrays plus optional position samplers, so grl-snam, the cvcdbg demos/harness, -and `sim_world` itself all drive it identically. +`sim_world::snapshot` arrays plus optional position samplers, so grl-snam, the cvcGL nav demos, a +downstream app or test harness, and `sim_world` itself all drive it identically. ## Types @@ -26,18 +29,19 @@ and `sim_world` itself all drive it identically. then feed `drive_sample` from `drive_telemetry_data()`); otherwise `mu_mean` stays 1, `mrisk_mean` 0 (the no-material/no-grip neutral). - **`episode_nav_stats`** — one per episode: the reduced fleet fields + `per_vehicle`, and `to_json()` - (the base record; a DBG consumer nests an `"rf"` member itself). The `1e30` "unmeasured" sentinel + (the base record; an extension layer nests its own member itself). The `1e30` "unmeasured" sentinel for `min_clearance_m`/`min_sep_m` serializes as JSON `null`, not a huge finite number. - **`nav_stats_params`** (thresholds) and **`budget_policy`** (time / ETA-multiple / fuel bounds). - **`nav_samplers`** — optional per-position hooks: `material_id(x,y)`, `occupied(x,y)`, and `min_clearance_m` (a `const double*` **in metres** — see the units note below). - **`nav_scorecard`** + **`aggregate_nav(episodes, checkpoint)`** — reduce a corpus of episodes into - one RF-free fitness row (arrival, economy, safety, material) for ranking base-policy checkpoints. + one domain-neutral fitness row (arrival, economy, safety, material) for ranking base-policy + checkpoints. Includes the fleet drive-telemetry means (`mean_alpha`/`mean_beta`/`mean_gamma`, `mean_mu`, `mean_mrisk`, `mean_ext_force`) and `material_time_share[kNumMaterials]` — the signals a grip/risk tuning or trained-policy A/B is judged on (lower `mean_mrisk` + off-hazard time-share at held - arrival). Note `composite_score` / grip-margin are reserved to a downstream (cvc::dbg) scorecard, - not computed here. + arrival). A weighted composite score or a grip margin is left to a downstream extension's own + scorecard, not computed here. ## Collecting @@ -85,24 +89,25 @@ homogeneous convoy. `veh_nav_stats::arrived` / `time_to_goal_s` latch on the **rising edge of the sim's `reached[i]`** flag (`nav_stats_params::reach_eps`), i.e. when a vehicle actually gets within `sim_world`'s `reach_tol` of *its own* goal. That is the per-vehicle truth. A **convoy/harness may carry its own -coarser arrival tolerance** for a whole-column "done" check — e.g. cvcdbg's `ConvoyController::arrive_m() -= N·standoff + 40 m`, ~172 m for a 6-vehicle column. That column tolerance is fine as a formation -check but **hides a tail follower that parked short**: the harness can print `atObjective=6/6` while -two followers never latched `reached`. When you are debugging "did each vehicle arrive?", read the -per-vehicle `arrived`/`time_to_goal_s` (`time_to_goal_s < 0` = never reached), **not** the aggregate -column count. - -Worked example — the cvcdbg demo3 tail-follower loss was isolated entirely with this schema via -`cvcdbg-nativedemo/tools/dbg_arrival_check3.cpp` (`--json` per-vehicle records): comm-off arrives 6/6 -in every condition, while the bounded comm-steer force loses the two tail followers under sustained -jamming (`reached=0`, `turn_total_rad` 12→252 = looping, `wall_entries` 0→19). Two traps that turn -these stats into noise if ignored: (1) the coarse column tolerance above, and (2) a harness whose jam -schedule scales with total run length — hold the run length fixed when A/B-ing. See -`cvcdbg-nativedemo/docs/demo3-follower-loss.md` for the full case and the `turn_total_rad` / -`wall_entries` / `time_stopped_s` interpretation used to distinguish "looping" from "frozen." +coarser arrival tolerance** for a whole-column "done" check — e.g. one that grows with the number of +vehicles times their standoff, so a long column's tolerance is many times a single vehicle's +`reach_tol`. That column tolerance is fine as a formation check but **hides a tail follower that +parked short**: the harness can report the whole column at the objective while some followers never +latched `reached`. When you are debugging "did each vehicle arrive?", read the per-vehicle +`arrived`/`time_to_goal_s` (`time_to_goal_s < 0` = never reached), **not** the aggregate column +count. + +The per-vehicle records (`to_json()`) are enough on their own to isolate a follower that fails to +arrive. A/B the same scenario with and without the suspect influence (e.g. an extra steering force +injected through `ext_force`): a follower that is **looping** shows `reached=0` with `turn_total_rad` +and `wall_entries` far above the baseline run, while a **frozen** one shows `time_stopped_s` climbing +instead. Two traps turn these stats into noise if ignored: (1) the coarse column tolerance above, and +(2) a harness whose disturbance schedule scales with total run length — hold the run length fixed +when A/B-ing. ## Tests `src/cvc/tests/nav_stats_test.cpp` (the base accumulators/scorecard over a scripted trajectory) and `nav_test.cpp`'s `NavSimWorld` suite (the `sim_world` internal collector + `min_clearance_world`). The -scripted corpus + numbers are mirrored in cvcdbg's and grl-snam's tests — the shared-schema contract. +scripted corpus + numbers are mirrored in grl-snam's tests (and in any downstream extension that +re-checks the base record) — the shared-schema contract. diff --git a/docs/NAV_VEHICLE.md b/docs/NAV_VEHICLE.md index 59088b59c..d0c7d09fe 100644 --- a/docs/NAV_VEHICLE.md +++ b/docs/NAV_VEHICLE.md @@ -68,7 +68,7 @@ untouched. |---|---| | `bicycle_rollout` | CPU, threaded | | `bicycle_rollout_material` | CPU + the material force | -| `bicycle_rollout_ext` | CPU + a generic external force (e.g. cvc::dbg's RF/comm force) | +| `bicycle_rollout_ext` | CPU + a generic external force (e.g. a downstream layer's steering force) | | `bicycle_rollout_material_ext` | CPU + **both** the material force AND the external force, fused | | `bicycle_rollout_cuda` | GPU, **given** coefficients — the unfused device twin | | `drive_step`, `drive_step_material` | fused: `coef_feats` → MLP → rollout | @@ -85,7 +85,7 @@ so the fused entry points just pass BOTH; a null `ext.sample` reproduces `drive_step_material` byte-for-byte, a null material stack reproduces `drive_step_ext`. `sim_world::step()` now selects `drive_step_material_ext` whenever a material stack AND an external force are both attached, so a run can -steer on a learned grip/risk policy WHILE the comm/RF force also pushes. +steer on a learned grip/risk policy WHILE the external force also pushes. `bicycle_rollout_cuda` was added with earlier work. CUDA previously had only `sdf_sample_cuda` and the *fused* `drive_step_cuda`, so the device vehicle math diff --git a/docs/roadmap/CVCGL-UI-DSL-ROADMAP.md b/docs/roadmap/CVCGL-UI-DSL-ROADMAP.md index 30cd1a90e..7b0c4810c 100644 --- a/docs/roadmap/CVCGL-UI-DSL-ROADMAP.md +++ b/docs/roadmap/CVCGL-UI-DSL-ROADMAP.md @@ -550,7 +550,7 @@ meta: schema: 0.12 # .ari schema revision this doc validates against (default = ui:) ari_version: 1.2.0 # the DOCUMENT's own version (author-managed; never gates) app: nav_city_drive # identity (default = source basename) - name: "Austin RF-Denial Convoy" + name: "Austin City Drive" author: "Joe Rivera " license: LGPL-2.1-or-later # SPDX description: "…" @@ -660,7 +660,7 @@ children: - combo: belief bind: sim.belief options: [shared, grouped, private] # static list = literal - visible_when: (= (state-get "sim.comm.mode") "expert") + visible_when: (= (state-get "sim.fog") "true") - checkbox: fog (sensing) bind: sim.fog tooltip: "toggle range-limited sensing" @@ -865,7 +865,7 @@ root: children: [ { slider_int: Agents, bind: sim.agents, range: [1, 512] } ] - type: overlay # placement:always ⇒ pinned regardless of root.layout corner: top_right - children: [ { text: "RF-DENIED", visible_when: (state-get "sim.jammed") } ] + children: [ { text: "PAUSED", visible_when: (state-get "sim.paused") } ] ``` ```yaml @@ -882,7 +882,7 @@ root: - window: Telemetry at: [0, 1] frame: { chrome: [title] } - children: [ { include: rf_telemetry } ] + children: [ { include: stats_panel } ] - window: Timeline at: [1, 0] span: [1, 2] # spans both columns of the bottom row @@ -1088,11 +1088,11 @@ watch mechanism (`state-watch`; polled per scheduler step — no `boost::signals macOS-safe), same intent discipline. ```yaml -- custom: comm-reactor +- custom: fog-reactor on_change: - path: sim.comm.enabled + path: sim.fog do: - - { if: [ {$bool: sim.comm.enabled}, { camera.chase: [] }, { camera.ortho: [true] } ] } + - { if: [ {$bool: sim.fog}, { camera.chase: [] }, { camera.ortho: [true] } ] } ``` **Input events** (`on:key` — keyboard now routed VTK-interactor→ImGui on native **and** wasm, landed on @@ -2382,7 +2382,7 @@ LGPL** (VP8/VP9 via BSD libvpx, AV1 via BSD dav1d) — a decode handler loses ** handler you never need more than this. - **Fallback — GPL only for GPL-only capability (H.264/HEVC encode), and prefer out-of-process.** The FSF-clean pattern is a **separate process** — shell out to the GPL `ffmpeg` executable over pipes (what - `ffmpeg-cli` + `grl_snam_dbg` already do). **`dlopen` of a GPL build is *not* a license firewall:** + `ffmpeg-cli` and downstream video-export tools already do). **`dlopen` of a GPL build is *not* a license firewall:** calling the `libav*` C ABI and passing `AVFrame`/`AVCodecContext` in one address space is exactly the "intimate, shared-internal-structures" case the FSF treats as a combined work. (The dynamic-linking = derivative theory is contested and unsettled in US courts — LGPL exists because of that uncertainty — so @@ -2417,8 +2417,8 @@ executable over pipes"). The Web Worker is just the browser stand-in for a subpr `memcpy`s into the pinned RGBA8 buffer, then marshals `texture_modified()` to the render thread. The decode args are **byte-for-byte identical** to the wasm Worker's. Resolve the binary as the project already does — `os.path.join(sys.prefix, "bin", "ffmpeg[.exe]")` (bare `ffmpeg` is deliberately not on `PATH`; the static - cvcpkg build lives in the active prefix). This reuses the exact out-of-process shape `ffmpeg-cli` + - `grl_snam_dbg` run — though those are **encode-direction, batch, disk-mediated** (`subprocess.run`, PNG→mp4); + cvcpkg build lives in the active prefix). This reuses the exact out-of-process shape `ffmpeg-cli` and + downstream video-export tools run — though those are **encode-direction, batch, disk-mediated** (`subprocess.run`, PNG→mp4); a *streaming decode* pump (`Popen` + `-f rawvideo`) is net-new code in the same shape. - **Transport: simple pipe for v1; shm is a later micro-opt.** The pipe costs **one** copy at the boundary (kernel pipe → `read()` into the pinned buffer); single-digit-ms, decode-dominated — realtime-adequate. True @@ -2610,8 +2610,8 @@ the separate `isosurfaces` list are declarable as sibling fields. ### 10.2 Dimensionality is a runtime property — `dims:` / `axes:` with room for nD Dimensionality is **one field on one TF type**, not N types — adopting the unified model from -the **volrover3 modernization roadmap §11–§12** (`cvc-engagement-docs-status/modernization/ -2026-08-11-volrover3-roadmap.md`; the `CVC-modernization*.md` trio only records the parity gap +the **volrover3 modernization roadmap §11–§12** (`2026-08-11-volrover3-roadmap.md`, maintained +outside this repository; the `CVC-modernization*.md` trio only records the parity gap and the `cvcQt` consolidation). A TF is a function of an ordered **tuple of axes**; `dims:` (or the length of `axes:`) *is* the dimensionality — 1D today, 2D/3D supported, shape open for nD/4D (a `Time` axis + keyframes). @@ -2949,20 +2949,20 @@ root: type: group layout: { kind: vertical } children: - - load: file://panels/rf_telemetry.ari # or state://… or https://… (bare = file:, §13) - as: rf # mount id under this parent - args: { unit: alpha, max_range: 4000 } # substituted before parse, like include args + - load: file://panels/stats_panel.ari # or state://… or https://… (bare = file:, §13) + as: stats # mount id under this parent + args: { vehicle: 0, max_range: 4000 } # substituted before parse, like include args prefix: null # optional scene prefix for the fragment's scene-binds reload: on_change # off (default) | on_change | poll: (hot-reload, §12.5) ``` -- **Namespaced state.** The fragment's tree lands at `ui.docs..includes.rf.*`, and its +- **Namespaced state.** The fragment's tree lands at `ui.docs..includes.stats.*`, and its resident handlers (§7.1) are chrooted to that subtree via a fresh `apply_chroot` (§7.8.1); its - `ev.*` channels are namespaced `ui.docs..ev.rf..` (the scheduler queue keys on + `ev.*` channels are namespaced `ui.docs..ev.stats..` (the scheduler queue keys on the raw string, §7.8.5). An auto-`PushID` on the mount guards ImGui id collisions. - **Nested chroot** = the loader chrooting each level's handlers to the deeper path at wire time (no in-evaluator nesting, §7.8.7). -- **Binds** resolve against the mount prefix: a bare `bind:` → `ui.docs..includes.rf`; `/` → +- **Binds** resolve against the mount prefix: a bare `bind:` → `ui.docs..includes.stats`; `/` → app root (loader-realized); scene-qualified → the fragment's `prefix:` arg (default = host's `meta.prefix`). A **relative `load:`/`source:` inside the fragment** resolves against the URI it was loaded from (its `mount_base`, §13). diff --git a/docs/roadmap/LSYSTEM-LABORATORY-ROADMAP.md b/docs/roadmap/LSYSTEM-LABORATORY-ROADMAP.md index 048f77266..ab4aa67bd 100644 --- a/docs/roadmap/LSYSTEM-LABORATORY-ROADMAP.md +++ b/docs/roadmap/LSYSTEM-LABORATORY-ROADMAP.md @@ -3,7 +3,7 @@ **Status:** **Revision 2.1** — design approved for implementation, revised after an adversarial review pass and a rebase, then amended by the **connected-terrain decision (D9)**. Target branch family `feat/lsystem-lab-*`, baselined on `origin/master` @ **`8b6f426`** (was `10b7904`; `a33851f`, `e97d06c` and `8b6f426` have since merged). **Owner:** L-System Laboratory session. **Document path:** `docs/roadmap/LSYSTEM-LABORATORY-ROADMAP.md` -**Last verified against the tree:** 2026-08-27, worktree `/home/joe/src/cvc/wt-libcvc-lsyslab`, rebased onto `8b6f426`. +**Last verified against the tree:** 2026-08-27, on a branch rebased onto `8b6f426`. **What changed in revision 2:** see §17 (Revision history). The archipelago is now specified rather than asserted (§4.3a), the indoor clearance scheme is derived from one number rather than three contradictory ones (§6b.1a), hard classes carry ρ = 0 (§7.2a), the export frame confronts σ-in-cells (§7.1a), and the collision map is rebuilt for the post-merge world (§14). **What changed in revision 2.1:** the user decided **D9** — *"let the terrain be connected throughout."* The generator now **forces isthmuses** so the landmass is one component; `forced-bridges` is the v1 default, `single-island` survives only as an opt-in ablation, the binding gate criterion is **`components == 1`** (not a 0.98 area fraction), and the D2 × D9 window-size blocker **dissolves**. Propagated through §0, §1.2, §4.3a, §7.8, §9.4, §10.1, §11.2, §12.3, §13.2, §15.1 R19, §15.3 D2/D9, §16.3 and the closing claims. See §17. @@ -85,7 +85,7 @@ The Laboratory is the tool that closes that loop: an interactive procedural worl ## 2. Relationship to the modernization roadmap -The roadmap of record is `/home/joe/src/cvc/cvc-engagement-docs/modernization/2026-08-11-volrover3-roadmap.md` (10 852 lines). It specifies **three independent LOD mechanisms**, and it is important not to conflate them. +The roadmap of record is the volrover3 modernization roadmap (`2026-08-11-volrover3-roadmap.md`, 10 852 lines, maintained outside this repository). It specifies **three independent LOD mechanisms**, and it is important not to conflate them. | # | System | § | Phases | Selection axis | Our relationship | |---|---|---|---|---|---| @@ -1133,7 +1133,7 @@ material_planes material_build(const float *risk_raw, const std::uint8_t *hard, It takes `rows, cols` **and** `cell_w` directly. It does **not** take `bounds` or `center`. -**The Python surface** — `/home/joe/src/cvc/GRL-SNAM/grl_snam/material.py:224-234` and `:255-257`: +**The Python surface** — GRL-SNAM's `grl_snam/material.py:224-234` and `:255-257`: ```python class MaterialGrid: @@ -3077,7 +3077,7 @@ brush_feather_m = 0.0 # the consumer blurs; extra feather is addi **Consumer constants — recorded in the manifest ONLY under `consumer_frame_ref`, as provenance, never read back** -Verified in `/home/joe/src/cvc/GRL-SNAM/grl_snam/material.py` (`MaterialParams`, `GateParams`) and `inc/cvc/nav/material.h` (`material_config`, `gate_params`) on 2026-08-27. Note these differ from the circulated brief, which was already stale — **and note the two twins differ from each other in two places** (§15.1 R17): +Verified in GRL-SNAM's `grl_snam/material.py` (`MaterialParams`, `GateParams`) and `inc/cvc/nav/material.h` (`material_config`, `gate_params`) on 2026-08-27. Note these differ from the circulated brief, which was already stale — **and note the two twins differ from each other in two places** (§15.1 R17): ``` Python (MaterialParams) C++ (material_config) diff --git a/docs/roadmap/NAV-STATS-INTRINSIC-ROADMAP.md b/docs/roadmap/NAV-STATS-INTRINSIC-ROADMAP.md index de4ca617b..bf39bab7d 100644 --- a/docs/roadmap/NAV-STATS-INTRINSIC-ROADMAP.md +++ b/docs/roadmap/NAV-STATS-INTRINSIC-ROADMAP.md @@ -2,16 +2,15 @@ Master plan for promoting the formation stats and the sim-intrinsic quantities into the shared `cvc::nav` `nav_stats` contract, syncing them live into the `cvc::state` tree, and wiring them into -training. Specs (design of record) live in **cvcdbg-nativedemo**: -`docs/nav-stats-design.md` §10 (schema), `docs/sim-intrinsic-stats.md` (catalog), and -`docs/stats-statetree-and-training.md` (state-tree + training). This doc is the *execution* plan and -tracks status; the specs are the *what/why*. +training. This doc is the *execution* plan and tracks status; the schema itself is documented in +[`docs/NAV_STATS.md`](../NAV_STATS.md) and `inc/cvc/nav/nav_stats.h`. ## Placement (the load-bearing decision) -Formation + all **non-RF** sim internals live in the **`cvc::nav` base** (`veh_nav_stats` / +Formation + all **generic** sim internals live in the **`cvc::nav` base** (`veh_nav_stats` / `episode_nav_stats` / `nav_scorecard` + the `grl_snam.metrics` Python twin) — formation is a navigation -concept (the grl-snam swarm can hold formations too). `cvc::dbg` adds **only RF** fields, joined to the -base by `(veh_index, convoy_id)`. Every field is **additive / off-by-default** (`formation_tol_m=0`, +concept (the grl-snam swarm can hold formations too). A downstream extension adds **only its own +domain-specific** fields, in its own record, joined to the base by `(veh_index, convoy_id)`; nothing +domain-specific lives in the base. Every field is **additive / off-by-default** (`formation_tol_m=0`, null samplers) so no pinned parity number moves until a case exercises it. The C++ and Python corpora are held byte-identical by the shared hand-computed fixture (`nav_stats_test.cpp` ⟷ `test_scorecard.py`). @@ -58,62 +57,49 @@ Mirror every Track-1 base field field-for-field in `NavStats`, add the `formatio update the shared hand-computed corpus so `test_scorecard.py` matches `nav_stats_test.cpp`. One PR per Track-1 sub-PR (or batched), gated on Track-1 landing. -### Track 3 — cvcdbg (RF ext + state bridge + harness re-point) -- **3a — RF ext fields**: fill the reserved `veh_rf_stats`/`episode_rf_stats` slots - (`fjam/fbw/fsw` via a 4-way `comm_accel` split; `eff_rate`, `bandwidth_slack`, `backbone_uptime` via - `comm_step_output`; `outage_prob`; `pingpong_rate`) + CoefNet `heads()` + RF-specific formation - (per-slot RF exposure, connectivity) joined by `(veh_index, convoy_id)`. -- **3b — state-tree bridge** (SPEC 1), split three ways: +### Track 3 — state-tree bridge + the extension seam +- **3a — extension record (downstream, not in this repo)**: a layer with domain-specific metrics keeps + them in its own per-vehicle/per-episode record, joined to the base by `(veh_index, convoy_id)`, and + nests it in the episode JSON itself. The base reserves nothing for it. +- **3b — state-tree bridge**, split three ways: - **3b-1** `cvc::gl/nav_stats_publish.{h,cpp}` — base + per-convoy formation scalars on the `state_publisher` value lane, keyed `.nav_stats.*` (SceneGraph prefix). - **3b-2** `publish_nav_rasters` — belief/fog planes as version-gated z=1 `cvc::volume` handles on the **data() lane** (VolumeNode-renderable), `sim_world::plane_version(m)` added; efficient realtime storage (deep-copy only on a changed plane). - - **3b-3** `cvc/dbg/nav_stats_publish.{h,cpp}` (RF sub-record → `.nav_stats.rf.*`, joined by - `veh_index`) + `dbg_austin_live3` wiring at the `finish()` seam (base + RF + rasters each frame via - the scene's auto-started publisher). + - **3b-3** (downstream) an extension publishes its own sub-record under its own + `.nav_stats..*` subtree, joined by `veh_index`, from the same `finish()` seam (base + + extension + rasters each frame via the scene's auto-started publisher). Still to do on top: per-sim `nav_stats.sims` registry; the ImGui stats panel / DSL binding. -- **3c — harness re-point**: `dbg_arrival_check3` sets `formation_tol_m`, passes `formation_slot`, - `set_identity(..., formation_parent)`, and drops the harness-side `FORMSTATS`/`form_*` bookkeeping in - favour of the collector's; the scorecard then carries it natively. +- **3c — harness re-point**: a test harness that kept formation bookkeeping on its own side sets + `formation_tol_m`, passes `formation_slot`, calls `set_identity(..., formation_parent)`, and drops its + own formation counters in favour of the collector's; the scorecard then carries them natively. -### Track 4 — training (SPEC 2) -- **4-Phase 0**: scorecard plumbing — a Python reader over C++ `aggregate_nav`/`aggregate_rf` (no - trainer consumes them today); guard the C++⟷Python fixture parity gate. -- **4-Phase 1** (SELECTION): recorded RF into `CommTrainer._composite_score`; formation / belief / - grip-margin as `nav_scorecard` selection fields. -- **4-Phase 2** (LOSS on existing rollouts): RF exposure (`w_exposure>0` + ext-force rollout; re-baseline) - and grip anticipation (`train_bicycle` + `material_train.h` forward/vjp), reusing the material CVaR - pattern. +### Track 4 — training +- **4-Phase 0**: scorecard plumbing — a Python reader over C++ `aggregate_nav` (no trainer consumes it + today); guard the C++⟷Python fixture parity gate. +- **4-Phase 1** (SELECTION): formation / belief / grip-margin as `nav_scorecard` selection fields. +- **4-Phase 2** (LOSS on existing rollouts): grip anticipation (`train_bicycle` + `material_train.h` + forward/vjp), reusing the material CVaR pattern. - **4-Phase 3** (new surrogates): coupled multi-agent **formation** surrogate; coverage stays - SELECTION-only; RF true-field stays eval-only (the comm-steering-objective-gap). + SELECTION-only. ## Status -- [x] Specs written & merged (schema §10, catalog, state-tree+training) — cvcdbg #125/#126; - raster-storage spec — cvcdbg #127. -- [x] Formation stats collected harness-side (`FORMSTATS`) — cvcdbg #120–#126. - [x] **Track 1a — libcvc formation base fields — MERGED transfix/libcvc#421.** - [x] **Track 1b — stall / closest-approach — MERGED transfix/libcvc#422.** - [x] **Track 1c — belief-coverage + sense-flips — MERGED transfix/libcvc#423.** - [x] **Track 1d — drive telemetry — MERGED transfix/libcvc#424 → completes Track 1.** - [x] **Track 2 — grl-snam Python parity (`scorecard.py` + hand-computed corpus) — MERGED GRL-SNAM#96.** -- [x] **Track 3a — cvcdbg RF ext fields — MERGED: 3a-1 (`outage_prob`, `pingpong_rate`) cvcdbg#128; - 3a-2 (`fjam/fbw/fsw_mean`, `mean_eff_rate_mbps`, `backbone_connected_uptime`, - `mean_bandwidth_slack_mbps`) cvcdbg#129.** Reserved to Track 4: `composite_score`, `cvar_jam_loss`, - `worst_jam_loss`. - [x] **Track 3b-1 — libcvc base+formation state-tree publisher (`cvc::gl/nav_stats_publish`) — MERGED transfix/libcvc#425.** -- [~] **Track 3b-2 — libcvc belief/fog raster bridge (`publish_nav_rasters`, version-gated cvc::volume - data() lane + `sim_world::plane_version`) — PR open transfix/libcvc#426 (CI); 4 review defects fixed +- [x] **Track 3b-2 — libcvc belief/fog raster bridge (`publish_nav_rasters`, version-gated cvc::volume + data() lane + `sim_world::plane_version`) — MERGED transfix/libcvc#426; 4 review defects fixed (consumer UAF, LLP64 offset, sentinel/nullptr gating).** -- [~] **Track 3b-3 — cvcdbg RF publisher (`cvc::dbg::publish_rf_stats`) + demo3 wiring — PR open - CVC-DBG/cvcdbg#130 (hermetic CI). Adversarially reviewed; veh_index join pinned.** ImGui stats - panel / DSL binding: still to do on top of 3b. -- [ ] Track 3c (harness re-point: `dbg_arrival_check3` formation_tol/slot/set_identity, drop FORMSTATS); - Track 4 (training Phases 0–3, incl. the deferred composite/CVaR/worst-jam RF fields) — pending. +- [ ] ImGui stats panel / DSL binding on top of 3b; Track 4 (training Phases 0–3) — pending. ## Invariants for every PR here Additive / off-by-default; C++⟷Python fixture parity held; torch ⟷ torch-free (`material_train.h`) -parity for any differentiable term; base stays RF-free (only `cvc::dbg` links RF). Any threshold that a -downstream twin must match to reproduce a field bit-for-bit (e.g. `stall_progress_eps_m = 0.05`) is a -shared cross-repo constant — change it only in lockstep across libcvc + grl_snam.metrics. +parity for any differentiable term; the base stays domain-neutral (extensions link their own +domain-specific code, never the base). Any threshold that a downstream twin must match to reproduce a +field bit-for-bit (e.g. `stall_progress_eps_m = 0.05`) is a shared cross-repo constant — change it only +in lockstep across libcvc + grl_snam.metrics. diff --git a/docs/roadmap/RENDER_PERF_ROADMAP.md b/docs/roadmap/RENDER_PERF_ROADMAP.md index a88979178..20b1af404 100644 --- a/docs/roadmap/RENDER_PERF_ROADMAP.md +++ b/docs/roadmap/RENDER_PERF_ROADMAP.md @@ -43,7 +43,7 @@ calls → triangles**, not the other way around. Rasterisation optimisations its vocabulary (`desired_pixel_error`, priority = projected area / distance²) so the streaming layer slots in later. - **Not** mesh decimation of authored `.glb` files as a first pass. For - imported meshes (buildings.glb, Humvee.glb, Soldier.glb) the ladder is + imported meshes (buildings.glb, Humvee.glb, character meshes) the ladder is culling → LOD-swap → impostor; decimation is a bake-time tool (`quadric_decimate`, volrover3 §26.4.2) applied to the archetype library. - **Not** GPU-instanced glyphs on wasm. `vtkOpenGLGlyph3DHelper::GlyphRender` @@ -156,7 +156,7 @@ Trees)*, §5 range degradation: *"With progressively increasing ranges, a tree will re-interpret stem meshes as lines and leaf polygons as points… A 100 000 facet tree geometry may be rendered at 2 kilometres as about 30 lines and 1000 points."* Re-interpret, do not convert. For imported meshes -(Humvee/Soldier), the pre-baked LOD tiers are the analogue. +(vehicles, characters), the pre-baked LOD tiers are the analogue. Success criteria: 500-agent Austin swarm at 60 FPS with shadows on. The chase cam still sees the full mesh at close range; a top-down orbit sees diff --git a/docs/roadmap/STATE_EXEC_PORTING_PLAN.md b/docs/roadmap/STATE_EXEC_PORTING_PLAN.md index a8fbc3722..e2aa08976 100644 --- a/docs/roadmap/STATE_EXEC_PORTING_PLAN.md +++ b/docs/roadmap/STATE_EXEC_PORTING_PLAN.md @@ -2026,7 +2026,7 @@ has no built-in ACL. ### 5. File Layout -All new files go under `/home/joe/src/libcvc/` (the working copy, not cvc/libcvc): +All new files go under the libcvc repository root: ``` inc/cvc/ diff --git a/docs/roadmap/STATE_LIFETIME_AND_ATOMICITY.md b/docs/roadmap/STATE_LIFETIME_AND_ATOMICITY.md index 8b3a591c4..54a3adc03 100644 --- a/docs/roadmap/STATE_LIFETIME_AND_ATOMICITY.md +++ b/docs/roadmap/STATE_LIFETIME_AND_ATOMICITY.md @@ -297,14 +297,14 @@ This is the "belt and suspenders" layer. It does **not** add atomicity. hazard-pointer / quiescence machinery** in `cvc::core` today (`thread_pool::_epoch` at `thread_pool.h:125` is a per-job wake counter, unrelated). The only ambient per-frame drain (`AriRuntime`) lives in the cvcGL layer, absent from CLI / headless / -pycvc / DBG. +pycvc / non-Ariadne downstream apps. So C requires designing a drain mechanism first. Options to evaluate: - an **epoch/quiescent-state** scheme where sweep unlinks + parks nodes in a graveyard reclaimed once all reader threads have passed a quiescent point; - **hazard pointers** per reader thread; - a **scheduler-tick barrier** as the natural drain point for the DSL/scheduler world - (but headless/CLI/DBG need their own). + (but headless/CLI/non-Ariadne apps need their own). ### 5.2 Costs to resolve before C ships @@ -391,4 +391,4 @@ primitive (§4.2) and annotate/covert `state_value_codec`/`state_list`. 2. Phase 2 — is collapsing the cache entry to one `data()` blob acceptable (loses per-field `state://…?children` addressability of cache internals)? 3. Phase 3 — which drain mechanism (epoch / hazard-pointer / scheduler-tick), given - headless/CLI/DBG have no ambient frame loop? + headless/CLI/non-Ariadne apps have no ambient frame loop? diff --git a/docs/roadmap/WATER-RENDERING-ROADMAP.md b/docs/roadmap/WATER-RENDERING-ROADMAP.md index 2b0e33807..f9118ea01 100644 --- a/docs/roadmap/WATER-RENDERING-ROADMAP.md +++ b/docs/roadmap/WATER-RENDERING-ROADMAP.md @@ -494,7 +494,7 @@ Baked `columnH` also makes the shoreline **immune to terrain LOD**: when a chunk #### 5.2.1 Why the two-draw composite was removed -An earlier revision of this document rendered the lid **twice** — draw A under `glBlendFunc(GL_ZERO, GL_SRC_COLOR)` to multiply the framebuffer by the chromatic transmittance `T`, draw B additively for the surface radiance. **That mechanism cannot be built on VTK 9.5 and has been deleted.** Four independent blockers, each verified against the shipped headers at `/home/joe/src/cvc/wt-volrover-perf/deps-live/include/vtk-9.5` (`vtkVersionMacros.h:` `VTK_VERSION "9.5.0"`): +An earlier revision of this document rendered the lid **twice** — draw A under `glBlendFunc(GL_ZERO, GL_SRC_COLOR)` to multiply the framebuffer by the chromatic transmittance `T`, draw B additively for the surface radiance. **That mechanism cannot be built on VTK 9.5 and has been deleted.** Four independent blockers, each verified against the shipped VTK 9.5 headers (`/include/vtk-9.5`) (`vtkVersionMacros.h:` `VTK_VERSION "9.5.0"`): | # | Blocker | Verification | |---|---|---| diff --git a/inc/cvc/ariadne/widget.h b/inc/cvc/ariadne/widget.h index 9e581077a..f1395865f 100644 --- a/inc/cvc/ariadne/widget.h +++ b/inc/cvc/ariadne/widget.h @@ -220,7 +220,7 @@ struct Widget { double img_size = 256.0; // §12 module mount (load:): when non-empty, this widget is a MOUNT POINT — a relative - // state-path segment (e.g. "includes.rf") appended to the enclosing scope, so its whole + // state-path segment (e.g. "includes.stats") appended to the enclosing scope, so its whole // SUBTREE resolves binds and evaluates reactive/read-lane expressions against that deeper // sub-prefix (its own chroot), while the mount widget's own reactive fields stay in the // parent scope. Empty for ordinary widgets. This is how a loaded fragment is isolated @@ -316,8 +316,9 @@ inline Widget text_bound(std::string label, std::string bind) { } // Tint ANY Text widget (literal or bound) a fixed RGB — the general severity-colour mutator, e.g. -// `with_text_color(text_bound("PDR", "rf.min_pdr"), 0.9f, 0.2f, 0.2f)`. Chainable; a no-op-safe -// pass-through for non-Text widgets (the walker only reads text_color on Kind::Text). +// `with_text_color(text_bound("Min clearance", "stats.min_clearance"), 0.9f, 0.2f, 0.2f)`. +// Chainable; a no-op-safe pass-through for non-Text widgets (the walker only reads text_color on +// Kind::Text). inline Widget with_text_color(Widget w, float r, float g, float b) { w.has_text_color = true; w.text_color[0] = r; diff --git a/inc/cvc/gl/DrapedLinkNode.h b/inc/cvc/gl/DrapedLinkNode.h index 9095e8db2..2718f1780 100644 --- a/inc/cvc/gl/DrapedLinkNode.h +++ b/inc/cvc/gl/DrapedLinkNode.h @@ -52,7 +52,7 @@ namespace gl { // replacements compose with it and clearShaderReplacements() keeps it. // // Colour and opacity are fragment uniforms (setStyle), unlit, so restyling a -// link per frame (signal quality, jamming) does not bump the property MTime -- +// link per frame (e.g. by a live per-link quality metric) does not bump the property MTime -- // which would re-bake the shadow maps. Opacity < 1 additionally moves the // actor to the translucent pass (a property change, made only when crossing 1). // diff --git a/inc/cvc/gl/Viewport.h b/inc/cvc/gl/Viewport.h index 8fbba5e0e..44d35fa84 100644 --- a/inc/cvc/gl/Viewport.h +++ b/inc/cvc/gl/Viewport.h @@ -33,7 +33,7 @@ class ViewportManager; // // Two flavours (both first-class): a SCENE viewport draws its own distinct // SceneGraph (a clean, independent renderer); a MIRROR viewport is an alternate -// view of a scene another viewport already draws (the DBG-minimap / PiP case). +// view of a scene another viewport already draws (the minimap / PiP case). // This header is the shared surface; the manager's addSceneViewport / // addMirrorViewport decide the flavour. // @@ -92,8 +92,8 @@ class Viewport { // Whether this viewport receives routed input (default true). A minimap / HUD // inset set false is skipped by ViewportManager::viewportAt, so clicks fall - // through to the viewport beneath it — the DBG minimap is non-interactive by - // design ("a minimap that pans or dollies stops being a map"). Does not affect + // through to the viewport beneath it — an overview minimap is non-interactive + // by design ("a minimap that pans or dollies stops being a map"). Does not affect // compositing. void setInputEnabled(bool on); bool inputEnabled() const; diff --git a/inc/cvc/gl/ViewportManager.h b/inc/cvc/gl/ViewportManager.h index b06e95a90..af2575df7 100644 --- a/inc/cvc/gl/ViewportManager.h +++ b/inc/cvc/gl/ViewportManager.h @@ -30,7 +30,7 @@ class Viewport; // context — mandatory under WASM's single canvas) and composites N Viewports as // LAYERED vtkRenderers in that window, differentiated by SetViewport rect and // SetLayer, drawn by a single window->Render(). This is the picture-in-picture -// path (the DBG minimap, generalised): alternate views of one scene, or entirely +// path (the overview minimap, generalised): alternate views of one scene, or entirely // separate scenes, all in one app instance and one context. // // It is constructed like a SceneRenderer (a main scene + size + offscreen flag) diff --git a/inc/cvc/gl/nav_stats_publish.h b/inc/cvc/gl/nav_stats_publish.h index d0650aa9a..4628b2b30 100644 --- a/inc/cvc/gl/nav_stats_publish.h +++ b/inc/cvc/gl/nav_stats_publish.h @@ -6,10 +6,10 @@ */ // nav_stats_publish.h — bridge a base cvc::nav::episode_nav_stats snapshot into the running scene's -// cvc::state tree, off the render path, for realtime ImGui / the cvcGL UI DSL to read (SPEC 1 in -// CVC-DBG/cvcdbg docs/stats-statetree-and-training.md). This is the BASE + formation half -// (RF-free); the cvc::dbg RF sub-record is published by cvc/dbg/nav_stats_publish.h, and the -// belief/fog RASTERS take a separate data()-lane handle path (the publisher carries only +// cvc::state tree, off the render path, for realtime ImGui / the cvcGL UI DSL to read (see +// docs/roadmap/NAV-STATS-INTRINSIC-ROADMAP.md, Track 3b). This is the BASE + formation half +// (domain-neutral); a downstream extension publishes its own sub-record under its own subtree, and +// the belief/fog RASTERS take a separate data()-lane handle path (the publisher carries only // value()-lane scalars). Keying is by the scene's SceneGraph prefix (getStatePrefix()), so // concurrent sims under distinct prefixes never collide. #pragma once diff --git a/inc/cvc/lsys/interp.h b/inc/cvc/lsys/interp.h index 39b642b3e..f9e3eb92f 100644 --- a/inc/cvc/lsys/interp.h +++ b/inc/cvc/lsys/interp.h @@ -34,7 +34,7 @@ // // Material semantics stay abstract: every primitive carries a generic `role` // (trunk/foliage/rock/concrete/…). cvc::world maps role -> its surface_registry -// class; cvc::lsys never knows the concrete class ids or the DBG RF vocabulary. +// class; cvc::lsys never knows the concrete class ids or any consumer's material vocabulary. #ifndef CVC_LSYS_INTERP_H #define CVC_LSYS_INTERP_H @@ -52,7 +52,8 @@ struct vec3 { }; // Generic material roles. cvc::world maps these to concrete surface classes and -// (on the DBG side) to RF material names. NOT DBG-specific. +// (through each class's rf_material label) to the shared physical-material names. +// NOT consumer-specific. enum class role : std::uint16_t { trunk = 0, // woody stem -> wood branch, // thin woody -> wood diff --git a/inc/cvc/nav/drive.h b/inc/cvc/nav/drive.h index 29d14f47a..023700ea4 100644 --- a/inc/cvc/nav/drive.h +++ b/inc/cvc/nav/drive.h @@ -269,9 +269,9 @@ void drive_step(const field_stack &f, float *o, float *th, float *sp, const floa // repulsion F_rep and (when present) the material force — and, when `steer`, // into the steering bias too, exactly like material_drive. cvc::nav knows only // "a force field evaluated at a pose"; it has NO vocabulary for what the force -// MEANS. A private consumer (e.g. cvc::dbg's RF/comms force) builds an ext_force +// MEANS. A downstream consumer with its own domain-specific force builds an ext_force // whose `user` carries its state and whose `sample` computes its physics, then -// calls drive_step_ext — so the open core never grows an RF/comm concept. +// calls drive_step_ext — so the open core never grows a domain-specific concept. // // `sample(user, i, plane, onx, ony, fx, fy)` writes the force for agent i at its // current NORMALIZED (centered) position (onx, ony) — cvc::nav's native drive diff --git a/inc/cvc/nav/material.h b/inc/cvc/nav/material.h index 7b840a67a..c351788e3 100644 --- a/inc/cvc/nav/material.h +++ b/inc/cvc/nav/material.h @@ -209,13 +209,13 @@ void drive_step_material(const field_stack &f, float *o, float *th, float *sp, c // ─── Fused material + external-force drive ─────────────────────────────────── // The learned grip/risk policy (the material path, INCLUDING a lam-head net) AND a generic -// ext_force (e.g. cvc::dbg's RF/comm force) applied in the SAME rollout — both forces summed into -// one accumulator and one steering bias (rollout_impl already fuses them; these entry points are -// the only ones that pass BOTH). Without this, a caller must choose material OR ext: sim_world's -// step() gives material priority, so attaching a material stack silently drops the ext force. Use -// the fused path when a demo must steer on a trained grip/risk net WHILE the comm transformer also -// pushes. A null ext.sample makes drive_step_material_ext byte-identical to drive_step_material; a -// null material stack makes it byte-identical to drive_step_ext. +// ext_force (e.g. a downstream layer's steering force) applied in the SAME rollout — both forces +// summed into one accumulator and one steering bias (rollout_impl already fuses them; these entry +// points are the only ones that pass BOTH). Without this, a caller must choose material OR ext: +// sim_world's step() gives material priority, so attaching a material stack silently drops the ext +// force. Use the fused path when a host must steer on a trained grip/risk net WHILE its external +// force also pushes. A null ext.sample makes drive_step_material_ext byte-identical to +// drive_step_material; a null material stack makes it byte-identical to drive_step_ext. void bicycle_rollout_material_ext(const field_stack &f, float *o, float *th, float *sp, const float *goal, const float *al, const float *be, const float *ga, int n, const int *map_id, const veh_params &v, diff --git a/inc/cvc/nav/material_raster.h b/inc/cvc/nav/material_raster.h index 725e71ec6..f9777d637 100644 --- a/inc/cvc/nav/material_raster.h +++ b/inc/cvc/nav/material_raster.h @@ -13,7 +13,7 @@ // material_raster.h — pure-C++ (cvc::nav) generator of a scene MATERIAL raster from a scene bundle: // classify the bundle's land cover into the 13-class palette (indexed like cvc::nav::kNumMaterials -// / the cvc::dbg MATERIAL_TABLE) on the terrain grid, and derive per-material grip (mu) + terrain +// and grl_snam's MATERIALS) on the terrain grid, and derive per-material grip (mu) + terrain // risk. This is the pure-libcvc twin of grl-snam's `grl-snam material-raster` // (grl_snam.tools.material_raster) — SAME algorithm, SAME material.json output // (cvc-scene-material/1) — so a libcvc-only toolchain can produce the material input the nav-stats @@ -32,8 +32,8 @@ namespace cvc { namespace nav { -// The 13-class palette (index == material id); MUST match cvc::dbg MATERIAL_TABLE / grl_snam -// MATERIALS. Only the drivable-surface ids are ever emitted by the classifier +// The 13-class palette (index == material id); MUST match grl_snam MATERIALS and any downstream +// table keyed by these ids. Only the drivable-surface ids are ever emitted by the classifier // (open_air/foliage/soil/water/rock); the building materials are obstacle surfaces tagged from // scene metadata, never ground. enum material_id : int { diff --git a/inc/cvc/nav/nav_stats.h b/inc/cvc/nav/nav_stats.h index 37e781d54..f45f46157 100644 --- a/inc/cvc/nav/nav_stats.h +++ b/inc/cvc/nav/nav_stats.h @@ -23,18 +23,19 @@ // nav_stats.h — the BASE navigation-statistics layer: per-vehicle + per-episode // telemetry over a sim_world drive, plus a corpus scorecard for training fitness. // -// This is the RF-FREE half of the two-layer nav-stats design (CVC-DBG/cvcdbg -// docs/nav-stats-design.md). It lives in cvc::nav — below cvc::dbg — so the -// non-DBG grl-snam swarm, any cvc::nav-only demo, AND the cvcdbg convoy demos all -// collect the SAME base row (motion, clearance, collisions/penetration, material -// dwell, time, fuel, budgets). The RF/comms-force extension (exposure, PDR/SINR, -// band/retunes) is a SEPARATE cvc::dbg record joined by veh_index; it never lives -// here. The collector consumes only the sim_world::snapshot arrays plus optional -// position samplers, so it has no libcvc-internal dependency beyond the stdlib. +// This is the domain-neutral BASE half of a two-layer nav-stats design (see +// docs/NAV_STATS.md). It lives in cvc::nav, so the grl-snam swarm, the cvcGL nav +// demos, AND any downstream app built on cvc::nav all collect the SAME base row +// (motion, clearance, collisions/penetration, material dwell, time, fuel, +// budgets). A downstream extension's domain-specific metrics (e.g. about an +// external force it injects) are a SEPARATE record it keeps, joined by veh_index; +// they never live here. The collector consumes only the sim_world::snapshot arrays +// plus optional position samplers, so it has no libcvc-internal dependency beyond +// the stdlib. // The scorecard fields mirror the Python grl_snam.scorecard schema (the per-vehicle // veh_nav_stats is a superset of grl_snam.metrics.NavStats) — reuse, not reinvent. C++ // and Python are held in step today by independent hand-computed corpora sharing the -// same literals (this repo's nav_stats_test, cvcdbg's, grl-snam's test_scorecard); a +// same literals (this repo's nav_stats_test, grl-snam's test_scorecard); a // single shared-fixture parity gate is a tracked follow-up. #pragma once @@ -110,7 +111,7 @@ struct nav_samplers { // null ⇒ formation stats OFF (the whole formation path degrades to zero, numbers unchanged). The // collector accumulates |pos_i − slot| into slot_error_mean/max and latches formation_arrived // when the final slot error is within params.formation_tol_m. Formation is a NAV concept, so it - // lives in the base — a downstream RF layer references the same (veh_index, convoy_id). + // lives in the base — a downstream extension references the same (veh_index, convoy_id). std::function formation_slot; // Per-agent belief "sense flips" for THIS step: the number of cells whose occupied/free belief // bit flipped on the agent's most recent sense sweep (0 on ticks where the agent did not sense). @@ -196,7 +197,7 @@ struct veh_nav_stats { // drive telemetry (all 0/off unless a nav_samplers.drive feed is provided) — per-vehicle // reductions of the per-tick drive_sample. The CoefMLP coefficient means show what the learned // policy commanded; mu/mrisk show the terrain the agent actually drove (mean + worst); - // ext_force_mean is the average magnitude of the external (e.g. RF/comms) push; steer means/peak + // ext_force_mean is the average magnitude of the external (ext_force) push; steer means/peak // are control effort; binding_steps counts ticks the IPC barrier was active. drive_steps is the // tick count these average over. long drive_steps = 0; @@ -246,12 +247,12 @@ struct episode_nav_stats { nav_coverage coverage; std::vector per_vehicle; - std::string to_json() const; // the base record (a DBG consumer nests an "rf" member itself) + std::string to_json() const; // the base record (an extension nests its own member itself) }; // Collector: begin_episode -> step (once per sim frame) -> finish. Consumes the -// sim_world::snapshot arrays, so grl-snam and the cvcdbg demos/harness drive it -// identically. +// sim_world::snapshot arrays, so grl-snam, the cvcGL nav demos and any downstream +// harness drive it identically. class nav_stats_collector { public: explicit nav_stats_collector(nav_stats_params p = {}) : p_(p) {} @@ -293,7 +294,8 @@ class nav_stats_collector { // ── Scorecard: aggregate a CORPUS of episodes for training-fitness tracking ── // // The base nav-fitness row grl-snam uses to rank its OWN base-policy checkpoints -// over a scene corpus (RF-free). The DBG campaign layers an rf_scorecard on top. +// over a scene corpus (domain-neutral). A downstream extension can layer its own +// scorecard on top. struct nav_scorecard { std::string checkpoint; int n_episodes = 0; diff --git a/inc/cvc/nav/sim_world.h b/inc/cvc/nav/sim_world.h index fd0b601a4..70b32ac7f 100644 --- a/inc/cvc/nav/sim_world.h +++ b/inc/cvc/nav/sim_world.h @@ -147,8 +147,8 @@ class sim_world { // (ext.sample != nullptr) step() routes the (non-material) drive through // drive_step_ext, summing this force into the SAME accumulator as F_rep/F_goal // (and the steering bias when ext.steer) — the sanctioned ext_force port - // (drive.h). The core stays physics-agnostic: a private consumer (e.g. - // cvc::dbg's RF/comms force) supplies the callback + its state via ext.user, + // (drive.h). The core stays physics-agnostic: a downstream consumer (with its + // own domain-specific force) supplies the callback + its state via ext.user, // which must outlive stepping. A null sample (the default) is byte-identical to // the plain drive. No effect on the material path (drive_step_material). void set_ext_force(const ext_force &ext) { ext_ = ext; } @@ -342,7 +342,7 @@ class sim_world { // (they never enter plane derivation — unlike sigma / hard_margin), so mutating them here takes // effect on the next step with no cost, exactly like set_separation. A no-op if material is not // attached (mat_on_ false). This is the lever a live "force bias" UI scales — e.g. a caller that - // also scales the ext (RF/comm) force can trade material-reroute vs comm-steering during a drive. + // also scales the ext force can trade material-reroute vs ext-force steering during a drive. void set_material_lam(float lam_soft, float lam_hard) { mat_cfg_.lam_soft = lam_soft; mat_cfg_.lam_hard = lam_hard; diff --git a/inc/cvc/world/raster.h b/inc/cvc/world/raster.h index a01ea2e5e..c7cf5a57c 100644 --- a/inc/cvc/world/raster.h +++ b/inc/cvc/world/raster.h @@ -51,7 +51,7 @@ namespace cvc { namespace world { struct world_params { - double min_x = -120.0, min_y = -120.0, max_x = 120.0, max_y = 120.0; // metres (±120 DBG scale) + double min_x = -120.0, min_y = -120.0, max_x = 120.0, max_y = 120.0; // metres (±120 m default) std::uint64_t seed = 0; heightfield_params hf; scatter_params sc = scatter_params::defaults(); diff --git a/inc/cvc/world/surface.h b/inc/cvc/world/surface.h index 46b587910..b684e2249 100644 --- a/inc/cvc/world/surface.h +++ b/inc/cvc/world/surface.h @@ -33,11 +33,11 @@ // // Each class also carries a generic `rf_material` name from the shared physical // vocabulary (wood / foliage / soil / rock / reinforced_concrete / brick / -// drywall / glass / metal / water / open_air). This is the seam the DBG side -// keys its RF penetration table off; cvc::world does NOT know the DBG integer -// ids or the n_materials cap — that mapping stays private to cvc::dbg. The -// `penetration_db_per_m[3]` triple is carried as sub-6 GHz PROVENANCE only (the -// authoritative attenuation is the DBG MATERIAL_TABLE, resolved from the name). +// drywall / glass / metal / water / open_air). This is the seam a downstream +// propagation model keys its own penetration table off; cvc::world does NOT know +// that consumer's integer ids or class cap — that mapping stays with the consumer. +// The `penetration_db_per_m[3]` triple is carried as sub-6 GHz PROVENANCE only +// (the authoritative attenuation is the consumer's own table, resolved from the name). #ifndef CVC_WORLD_SURFACE_H #define CVC_WORLD_SURFACE_H diff --git a/src/cvc/nav/drive.cpp b/src/cvc/nav/drive.cpp index ba376d5f5..bffb666de 100644 --- a/src/cvc/nav/drive.cpp +++ b/src/cvc/nav/drive.cpp @@ -426,7 +426,7 @@ void rollout_impl(const field_stack &f, float *o, float *th, float *sp, const fl Fy = Fy + Fmat_y; } - // Generic external force (e.g. cvc::dbg's RF/comms force). Sampled at the + // Generic external force (e.g. a downstream layer's own force). Sampled at the // current pose AFTER geometry + material, and summed into the same F — the // physics-agnostic port; see ext_force in drive.h. A null ext leaves Fx/Fy // untouched, so the plain and _material paths stay byte-identical. @@ -830,7 +830,7 @@ void drive_step_material_ext(const field_stack &f, float *o, float *th, float *s const veh_params &v, const material_drive &mat, const ext_force &ext, float *minclr_out, int num_threads, drive_telemetry *tel) { // drive_step_material (feature-flag validation + lam-head extraction, IDENTICAL) but the ext - // force is also passed to rollout_impl, so the learned grip/risk policy and the ext (RF/comm) + // force is also passed to rollout_impl, so the learned grip/risk policy and the ext // force drive the SAME tick — summed into one force accumulator and one steering bias. A null // ext.sample reproduces drive_step_material exactly. const bool want_grip = model.has_mu(); diff --git a/src/cvc/nav/material_raster.cpp b/src/cvc/nav/material_raster.cpp index 244a0838d..c7de746f3 100644 --- a/src/cvc/nav/material_raster.cpp +++ b/src/cvc/nav/material_raster.cpp @@ -283,7 +283,7 @@ std::string material_raster::to_json() const { return t.str(); }; o << "{\"schema\":\"cvc-scene-material/1\",\"provenance\":\"scene land cover (cvc::nav " - "material_raster: masks if present, else satellite); ids = cvc::dbg MATERIAL_TABLE\","; + "material_raster: masks if present, else satellite); ids = the 13-class material palette\","; o << "\"rows\":" << rows << ",\"cols\":" << cols << ",\"bounds\":{\"min_x\":" << fnum(min_x) << ",\"min_y\":" << fnum(min_y) << ",\"max_x\":" << fnum(max_x) << ",\"max_y\":" << fnum(max_y) << "},"; diff --git a/src/cvc/nav/nav_stats.cpp b/src/cvc/nav/nav_stats.cpp index 3e1f4751d..27fa4ac43 100644 --- a/src/cvc/nav/nav_stats.cpp +++ b/src/cvc/nav/nav_stats.cpp @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -// nav_stats.cpp — see nav_stats.h. The base (RF-free) telemetry collector + +// nav_stats.cpp — see nav_stats.h. The base (domain-neutral) telemetry collector + // corpus scorecard, extracted from the two-layer nav-stats design. #include #include diff --git a/src/cvc/nav/sim_world.cpp b/src/cvc/nav/sim_world.cpp index 189537cbe..3fdcf9aa3 100644 --- a/src/cvc/nav/sim_world.cpp +++ b/src/cvc/nav/sim_world.cpp @@ -541,7 +541,7 @@ void sim_world::step(int num_threads) { md.lam_hard_scale = mat_cfg_.lam_hard_scale; if (ext_.sample) { // BOTH a material stack and an external force are attached — fuse them so the learned - // grip/risk policy AND the ext (RF/comm) force drive the same tick, instead of material + // grip/risk policy AND the ext force drive the same tick, instead of material // silently overriding ext. drive_step_material_ext == drive_step_material with the ext force // also summed into the accumulator + steering bias. drive_step_material_ext(fs, o_.data(), th_.data(), sp_.data(), carrot_.data(), model_, n_, @@ -552,7 +552,7 @@ void sim_world::step(int num_threads) { map_id_.data(), cfg_.veh, md, minclr_.data(), num_threads, telp); } } else if (ext_.sample) { - // External force channel (e.g. cvc::dbg's RF/comms force) summed into the + // External force channel (e.g. a downstream layer's own force) summed into the // drive via the sanctioned ext_force port. Byte-identical to drive_step when // ext_.sample is null (so this branch is only taken when a force is set). drive_step_ext(fs, o_.data(), th_.data(), sp_.data(), carrot_.data(), model_, n_, diff --git a/src/cvc/tests/nav_material_deploy_test.cpp b/src/cvc/tests/nav_material_deploy_test.cpp index f30ab8c08..2ccff4d18 100644 --- a/src/cvc/tests/nav_material_deploy_test.cpp +++ b/src/cvc/tests/nav_material_deploy_test.cpp @@ -5,7 +5,7 @@ // nothing exercised the WHOLE deploy loop: a widened net serialized to .cvcnav, reloaded, and // driven on the C++ host — the exact path a demo/game engine takes when it loads a trained policy. // These tests are that guarantee, plus the fused material+ext drive (drive_step_material_ext) that -// lets a run steer on the learned grip/risk policy WHILE an external RF/comm force also pushes. +// lets a run steer on the learned grip/risk policy WHILE an external force also pushes. #include #include @@ -304,10 +304,10 @@ TEST(NavMaterialDeploy, LearnedLamHeadDrivesReroute) { << " lamhi=" << x_lam_hi << ")"; } -// (5) The SHIPPED cvc-dbg-weights nav policy layout: 6-input RISK-ONLY + lam head — has_risk() but +// (5) A SHIPPED risk-aware nav policy layout: 6-input RISK-ONLY + lam head — has_risk() but // NOT has_mu() (no grip column), out=4. grl-snam `coef_train --w-risk --learned-lam` produces -// exactly this (add_lam_head(add_risk_feature(CoefMLP()))), and it's what ships as -// coef_mlp_riskaware.cvcnav / the --grip auto-load. The other tests above drive a 7-in +// exactly this (add_lam_head(add_risk_feature(CoefMLP()))), and it's the layout a host auto-loads +// when it drives grip/risk-aware. The other tests above drive a 7-in // grip+risk net; pin the shipped risk-only+lam contract too — it round-trips through .cvcnav and // drives the material path (which a risk net REQUIRES; the grip field is present but unused as a // feature since has_mu()==false). @@ -576,23 +576,22 @@ TEST(NavMaterialDeploy, LamSoftScaleMultipliesLearnedReroute) { EXPECT_LT(x_s2, x_s1 - 1e-4) << "scale=2 did not reroute more than scale=1 (scale ignored?)"; } -// Regression for cvcdbg #141 ("attach material stack to the STARTUP world under --grip"). The -// crash was a --grip startup world that auto-loaded the widened risk net but stepped it -// through the NON-material drive (no material stack attached) — which the plain drive REJECTS, so -// the throw escaped as an uncaught exception at the first frame. The demo binary is not in CI, so +// Regression: a host that auto-loads a terrain-risk net at startup but steps it through the +// NON-material drive (no material stack attached) throws at the first frame, because the plain +// drive REJECTS a risk net; uncaught, that throw crashes the host. Such hosts are not in CI, so // pin the underlying library invariant here: a terrain-risk net is rejected by drive_step and // accepted by drive_step_material (with a stack attached). If a future change lets a risk net fall -// through the plain drive, this fails instead of the demo crashing. +// through the plain drive, this fails instead of a host crashing. TEST(NavMaterialDeploy, RiskNetRejectedByPlainDriveAcceptedByMaterialDrive) { deploy_world w; coef_mlp net = make_net(7, 4, coef_mlp::kFlagFeatMu | coef_mlp::kFlagFeatRisk, 0.5f); ASSERT_TRUE(net.has_risk()); - // Non-material drive on a risk net: throws (this is the exact throw the --grip startup hit). + // Non-material drive on a risk net: throws (the throw such a host hits at startup). std::vector o1 = w.o, th1 = w.th, sp1 = w.sp, mc1(w.N); EXPECT_THROW(drive_step(w.fs, o1.data(), th1.data(), sp1.data(), w.carrot.data(), net, w.N, nullptr, w.v, mc1.data(), 1), std::runtime_error); - // Same net through the material drive with a stack attached: accepted (the fix's path). + // Same net through the material drive with a stack attached: accepted (the correct path). std::vector store; material_stack ms = w.risk_stack(store); std::vector ls(w.N, 0.5f), lh(w.N, 1.0f); diff --git a/src/cvc/tests/nav_stats_test.cpp b/src/cvc/tests/nav_stats_test.cpp index 1ed661f26..36b146c6e 100644 --- a/src/cvc/tests/nav_stats_test.cpp +++ b/src/cvc/tests/nav_stats_test.cpp @@ -5,11 +5,10 @@ This file is part of VolMagick. LGPL 2.1 (see other headers). */ -// nav_stats_test — the base (RF-free) cvc::nav::nav_stats collector + scorecard, +// nav_stats_test — the base (domain-neutral) cvc::nav::nav_stats collector + scorecard, // driven over a hand-computed scripted trajectory. Every accumulator is checked // against a value worked out on paper; the same corpus + numbers are mirrored in -// CVC-DBG/cvcdbg tests/nav_stats_test.cpp and CVC-Lab/GRL-SNAM tests/test_scorecard.py -// (the shared cross-repo schema contract). +// CVC-Lab/GRL-SNAM tests/test_scorecard.py (the shared cross-repo schema contract). #include #include diff --git a/src/cvc/tests/world_surface_test.cpp b/src/cvc/tests/world_surface_test.cpp index bcf0ec39c..c5d2be4a9 100644 --- a/src/cvc/tests/world_surface_test.cpp +++ b/src/cvc/tests/world_surface_test.cpp @@ -66,8 +66,8 @@ TEST(WorldSurface, RoleMapIsCompleteAndSensible) { } TEST(WorldSurface, EveryRfMaterialIsInTheTwelveNameVocabulary) { - // The names cvc::world emits must be resolvable by the DBG featurizer (plus the - // one new name, "rock", that this work adds on the DBG side). + // The names cvc::world emits must be resolvable by a consumer keyed on the shared + // physical-material vocabulary (plus the one new name, "rock", this work adds to it). const std::set vocab = {"open_air", "brick", "reinforced_concrete", diff --git a/src/cvc/world/surface.cpp b/src/cvc/world/surface.cpp index aa8262305..863fb2e35 100644 --- a/src/cvc/world/surface.cpp +++ b/src/cvc/world/surface.cpp @@ -63,8 +63,8 @@ const char *nav_name(nav_class n) { return "free"; } -// Sub-6 GHz penetration (low, mid, high) dB/m, PROVENANCE only; the DBG side is -// authoritative. Sourced from grl_snam_dbg simulator/materials.py MATERIAL_TABLE +// Sub-6 GHz penetration (low, mid, high) dB/m, PROVENANCE only; a consumer's own +// table is authoritative. Taken from a downstream RF simulator's material table // (sub6 triple). `rock` is the new id-12 material (granite-ish, between brick // and reinforced_concrete). void penetration_for(const char *rf, float out[3]) { diff --git a/src/cvcGL/CameraController.cpp b/src/cvcGL/CameraController.cpp index fdbb99ac8..783aeb20a 100644 --- a/src/cvcGL/CameraController.cpp +++ b/src/cvcGL/CameraController.cpp @@ -839,8 +839,8 @@ void CameraController::update(double dtSeconds) { // A still camera over a still scene costs nothing here: re-applying the same // pose is a no-op for vtkCamera, but the clipping-range reset re-derives every // visible prop's bounds (a mapper Update + cell-bounds pass per rewritten mesh) - // -- 1-1.5 ms a frame in the demo3 wasm profile, for a range that cannot have - // changed. applyNeeded() says when it can have. + // -- a measurable per-frame cost in a large WebGL2 scene with heavy per-frame + // overlays, for a range that cannot have changed. applyNeeded() says when it can have. if (applyNeeded()) applyToCamera(); // Mirror the live pose to state on a throttle (per-frame writes are a known diff --git a/src/cvcGL/examples/CMakeLists.txt b/src/cvcGL/examples/CMakeLists.txt index e3b75aa7f..b37277172 100644 --- a/src/cvcGL/examples/CMakeLists.txt +++ b/src/cvcGL/examples/CMakeLists.txt @@ -60,10 +60,11 @@ target_link_libraries(lsystem_coast PRIVATE cvcGL ${VTK_LIBRARIES} Boost::progra vtk_module_autoinit(TARGETS lsystem_coast MODULES ${VTK_LIBRARIES}) # terrain_lab — the visual QA surface for the cvc::world L-system terrain source -# (dbg-technical/21 §5). RENDERS the generator's own output: terrain coloured by -# the raster's per-cell material (or risk/occupancy/height), props grown from the -# built-in L-system recipes and tessellated in place, volumetric water below the -# sea level, a drifting cloud slab — all regenerated live from the ImGui panel. +# (docs/roadmap/LSYSTEM-LABORATORY-ROADMAP.md). RENDERS the generator's own output: +# terrain coloured by the raster's per-cell material (or risk/occupancy/height), +# props grown from the built-in L-system recipes and tessellated in place, +# volumetric water below the sea level, a drifting cloud slab — all regenerated +# live from the ImGui panel. # cvc::world + cvc::lsys are compiled into cvc::cvc (linked transitively via cvcGL), # so no extra find_package/link. Enrolled in _wasm_demos below. add_executable(terrain_lab terrain_lab.cpp) diff --git a/src/cvcGL/examples/nav_city_drive.cpp b/src/cvcGL/examples/nav_city_drive.cpp index 5ab3602c2..6e8d3a615 100644 --- a/src/cvcGL/examples/nav_city_drive.cpp +++ b/src/cvcGL/examples/nav_city_drive.cpp @@ -398,9 +398,7 @@ int main(int argc, char **argv) { if (bundle.empty()) { for (const char *p : {"deps/share/cvc-scenes/austin_south", "../deps/share/cvc-scenes/austin_south", - "share/cvc-scenes/austin_south", "platoon-sim/scene_viewer/exports/scenes/austin_south", - "../platoon-sim/scene_viewer/exports/scenes/austin_south", "scenes/austin_south", - "../scenes/austin_south"}) { + "share/cvc-scenes/austin_south", "scenes/austin_south", "../scenes/austin_south"}) { if (std::filesystem::exists(std::string(p) + "/terrain.json")) { bundle = p; std::printf("nav_city_drive: no --bundle given, autodetected %s\n", bundle.c_str()); @@ -812,13 +810,13 @@ int main(int argc, char **argv) { } } - // Belief-WALL reveal (the same translucent discovered-wall highlight the cvcdbg convoy demo - // draws in red, but coloured by SWARM here): as the agents sense the city, the discovered wall - // cells rise as translucent boxes tinted by the belief plane(s) that have seen them, and where - // MULTIPLE planes overlap on one wall the tint CYCLES through each seeing swarm's colour with a - // gentle pulse. Uniform tall walls, exactly like cvcdbg (0.05*span). The mesh is rebuilt only - // when coverage grows; the colours refresh every few frames via updateColors keyed to a - // per-vertex cell index — cheap, and it drives the animation without re-meshing. + // Belief-WALL reveal (a translucent discovered-wall highlight, coloured by SWARM): as the + // agents sense the city, the discovered wall cells rise as translucent boxes tinted by the + // belief plane(s) that have seen them, and where MULTIPLE planes overlap on one wall the tint + // CYCLES through each seeing swarm's colour with a gentle pulse. Uniform tall walls + // (kBeliefWallH). The mesh is rebuilt only when coverage grows; the colours refresh every few + // frames via updateColors keyed to a per-vertex cell index — cheap, and it drives the + // animation without re-meshing. bool beliefWallsOn = true; double beliefPhase = 0.0; // 0..1, wraps; colour cycle + brightness pulse std::shared_ptr beliefNode; @@ -827,8 +825,7 @@ int main(int argc, char **argv) { std::vector bwRgb; // per-vertex colour scratch std::vector cellRgb(static_cast(3) * rows * cols, 0); long bwLastCells = -1; // re-mesh trigger - const double kBeliefWallH = - std::min(40.0, 0.013 * span); // EXACTLY cvcdbg demo3s sensed-wall height (~40 m) + const double kBeliefWallH = std::min(40.0, 0.013 * span); // sensed-wall height, capped at 40 m // Per-cell cycling colour into cellRgb (only discovered wall cells; others untouched). auto paint_belief_cells = [&](double phase) { @@ -927,7 +924,7 @@ int main(int argc, char **argv) { const double xc = bounds.min_x + c * dx, yc = bounds.min_y + r * dy; const double x0 = xc - 0.5 * dx, x1 = xc + 0.5 * dx, y0 = yc - 0.5 * dy, y1 = yc + 0.5 * dy; const double z0 = terrain.empty() ? 0.0 : terrain.sample(xc, yc); - const double z1 = z0 + kBeliefWallH; // uniform tall wall, cvcdbg-style + const double z1 = z0 + kBeliefWallH; // uniform tall wall const double t0[3] = {x0, y0, z1}, t1[3] = {x1, y0, z1}, t2[3] = {x1, y1, z1}, t3[3] = {x0, y1, z1}; quad(t0, t1, t2, t3, static_cast(i)); // top face (always exposed) @@ -958,7 +955,7 @@ int main(int argc, char **argv) { beliefNode->setUseSingleColor(false); // per-vertex swarm colour, refreshed each cycle beliefNode->setAmbient(0.9); beliefNode->setDiffuse(0.2); - beliefNode->setOpacity(0.55); // translucent, like cvcdbg — the grey city shows through + beliefNode->setOpacity(0.55); // translucent — the grey city shows through beliefNode->setDepthOffset(1.0); } } else { @@ -992,7 +989,8 @@ int main(int argc, char **argv) { bool haveVehicle = false; if (vehicle.empty() && !bundle.empty()) { // Try inside the bundle first (a scene that ships its own vehicle), then the - // sibling shared/ convention (vehicle-humvee cvcpkg + platoon-sim layout). + // sibling shared/ convention the vehicle-humvee cvcpkg bundle installs (the wasm preload in + // examples/CMakeLists.txt mounts the same layout). const std::string candIn = bundle + "/Humvee.glb"; const std::string candSib = bundle + "/../../shared/Humvee.glb"; if (std::filesystem::exists(candIn)) @@ -1870,7 +1868,7 @@ int main(int argc, char **argv) { }; // Draw a grabbable colour-coded dot per rally point ON the minimap (drawn on top of the // PiP, so they're always visible) — this is what makes the destinations show + be - // draggable, like the cvcdbg convoy minimap. + // draggable. ImDrawList *dl = ImGui::GetWindowDrawList(); for (int t = 0; t < kTargets; ++t) { ImVec2 sp; diff --git a/src/cvcGL/examples/nav_city_swarm.cpp b/src/cvcGL/examples/nav_city_swarm.cpp index 1aef0b0ea..568fb68d8 100644 --- a/src/cvcGL/examples/nav_city_swarm.cpp +++ b/src/cvcGL/examples/nav_city_swarm.cpp @@ -400,9 +400,7 @@ int main(int argc, char **argv) { if (bundle.empty()) { for (const char *p : {"deps/share/cvc-scenes/austin_south", "../deps/share/cvc-scenes/austin_south", - "share/cvc-scenes/austin_south", "platoon-sim/scene_viewer/exports/scenes/austin_south", - "../platoon-sim/scene_viewer/exports/scenes/austin_south", "scenes/austin_south", - "../scenes/austin_south"}) { + "share/cvc-scenes/austin_south", "scenes/austin_south", "../scenes/austin_south"}) { if (std::filesystem::exists(std::string(p) + "/terrain.json")) { bundle = p; std::printf("nav_city_swarm: no --bundle given, autodetected %s\n", bundle.c_str()); @@ -818,13 +816,13 @@ int main(int argc, char **argv) { } } - // Belief-WALL reveal (the same translucent discovered-wall highlight the cvcdbg convoy demo - // draws in red, but coloured by SWARM here): as the agents sense the city, the discovered wall - // cells rise as translucent boxes tinted by the belief plane(s) that have seen them, and where - // MULTIPLE planes overlap on one wall the tint CYCLES through each seeing swarm's colour with a - // gentle pulse. Uniform tall walls, exactly like cvcdbg (0.05*span). The mesh is rebuilt only - // when coverage grows; the colours refresh every few frames via updateColors keyed to a - // per-vertex cell index — cheap, and it drives the animation without re-meshing. + // Belief-WALL reveal (a translucent discovered-wall highlight, coloured by SWARM): as the + // agents sense the city, the discovered wall cells rise as translucent boxes tinted by the + // belief plane(s) that have seen them, and where MULTIPLE planes overlap on one wall the tint + // CYCLES through each seeing swarm's colour with a gentle pulse. Uniform tall walls + // (kBeliefWallH). The mesh is rebuilt only when coverage grows; the colours refresh every few + // frames via updateColors keyed to a per-vertex cell index — cheap, and it drives the + // animation without re-meshing. bool beliefWallsOn = true; double beliefPhase = 0.0; // 0..1, wraps; colour cycle + brightness pulse std::shared_ptr beliefNode; @@ -833,8 +831,7 @@ int main(int argc, char **argv) { std::vector bwRgb; // per-vertex colour scratch std::vector cellRgb(static_cast(3) * rows * cols, 0); long bwLastCells = -1; // re-mesh trigger - const double kBeliefWallH = - std::min(40.0, 0.013 * span); // EXACTLY cvcdbg demo3s sensed-wall height (~40 m) + const double kBeliefWallH = std::min(40.0, 0.013 * span); // sensed-wall height, capped at 40 m // Per-cell cycling colour into cellRgb (only discovered wall cells; others untouched). auto paint_belief_cells = [&](double phase) { @@ -933,7 +930,7 @@ int main(int argc, char **argv) { const double xc = bounds.min_x + c * dx, yc = bounds.min_y + r * dy; const double x0 = xc - 0.5 * dx, x1 = xc + 0.5 * dx, y0 = yc - 0.5 * dy, y1 = yc + 0.5 * dy; const double z0 = terrain.empty() ? 0.0 : terrain.sample(xc, yc); - const double z1 = z0 + kBeliefWallH; // uniform tall wall, cvcdbg-style + const double z1 = z0 + kBeliefWallH; // uniform tall wall const double t0[3] = {x0, y0, z1}, t1[3] = {x1, y0, z1}, t2[3] = {x1, y1, z1}, t3[3] = {x0, y1, z1}; quad(t0, t1, t2, t3, static_cast(i)); // top face (always exposed) @@ -964,7 +961,7 @@ int main(int argc, char **argv) { beliefNode->setUseSingleColor(false); // per-vertex swarm colour, refreshed each cycle beliefNode->setAmbient(0.9); beliefNode->setDiffuse(0.2); - beliefNode->setOpacity(0.55); // translucent, like cvcdbg — the grey city shows through + beliefNode->setOpacity(0.55); // translucent — the grey city shows through beliefNode->setDepthOffset(1.0); } } else { @@ -998,7 +995,8 @@ int main(int argc, char **argv) { bool haveVehicle = false; if (vehicle.empty() && !bundle.empty()) { // Try inside the bundle first (a scene that ships its own vehicle), then the - // sibling shared/ convention (vehicle-humvee cvcpkg + platoon-sim layout). + // sibling shared/ convention the vehicle-humvee cvcpkg bundle installs (the wasm preload in + // examples/CMakeLists.txt mounts the same layout). const std::string candIn = bundle + "/Humvee.glb"; const std::string candSib = bundle + "/../../shared/Humvee.glb"; if (std::filesystem::exists(candIn)) @@ -1875,8 +1873,7 @@ int main(int argc, char **argv) { return u >= 0.0 && u <= 1.0 && v >= 0.0 && v <= 1.0; }; // Draw a grabbable colour-coded dot per target ON the minimap (drawn on top of the PiP, - // so they're always visible) — this is what makes the destinations show + be draggable, - // like the cvcdbg convoy minimap. + // so they're always visible) — this is what makes the destinations show + be draggable. ImDrawList *dl = ImGui::GetWindowDrawList(); for (int t = 0; t < kTargets; ++t) { ImVec2 sp; diff --git a/src/cvcGL/examples/terrain_lab.cpp b/src/cvcGL/examples/terrain_lab.cpp index 0f79af10d..982e707c0 100644 --- a/src/cvcGL/examples/terrain_lab.cpp +++ b/src/cvcGL/examples/terrain_lab.cpp @@ -4,7 +4,7 @@ // tree grammar). Terrain Lab instead drives the real headless generator: // cvc::world::world_model::generate(params) -> heightfield + surface registry + // scattered L-system props; cvc::world::raster() -> the occupancy/material/height -// grid the DBG training + gym consumers see. This demo turns that same output into +// grid downstream training + gym consumers see. This demo turns that same output into // geometry so we can SEE and TUNE it: the terrain mesh is coloured by the raster's // per-cell MATERIAL (or risk / occupancy / height), the props are grown from the // built-in L-system recipes and tessellated in place, water fills every cell that @@ -14,7 +14,7 @@ // The ImGui panel re-generates the world live: seed, per-species counts, tree // generations, relief amplitude, sea level, ontology and a preset — so you can tune // the generator and watch the scene rebuild. This is the visual QA surface for the -// L-system terrain source (dbg-technical/21 §5, LSYSTEM-LABORATORY-ROADMAP.md). +// L-system terrain source (docs/roadmap/LSYSTEM-LABORATORY-ROADMAP.md). // // Run (onscreen, navigable): terrain_lab // Tab toggles orbit/fly; WASD + mouse to fly; Esc releases the pointer. diff --git a/src/cvcGL/nav_stats_publish.cpp b/src/cvcGL/nav_stats_publish.cpp index 470216caf..b0bc60f87 100644 --- a/src/cvcGL/nav_stats_publish.cpp +++ b/src/cvcGL/nav_stats_publish.cpp @@ -118,9 +118,9 @@ void publish_nav_stats(cvc::gl::state_publisher &pub, const std::string &scenePr if (stride > 1 && (int)(i % stride) != 0) continue; const auto &v = e.per_vehicle[i]; - // Key by veh_index (the RF-record join key), not array position — identical on a - // collector-produced record (position == veh_index) but self-consistent with the RF publisher - // for reordered/sparse ones. + // Key by veh_index (the extension-record join key), not array position — identical on a + // collector-produced record (position == veh_index) but self-consistent with an extension's + // publisher for reordered/sparse ones. const std::string vp = root + ".veh." + std::to_string(v.veh_index); pub.publish(vp + ".arrived", b2s(v.arrived)); pub.publish(vp + ".time_to_goal_s", d2s(v.time_to_goal_s)); diff --git a/src/cvcGL/test/cvcgl_bounds_walk.cpp b/src/cvcGL/test/cvcgl_bounds_walk.cpp index f1124b0fe..91a8db161 100644 --- a/src/cvcGL/test/cvcgl_bounds_walk.cpp +++ b/src/cvcGL/test/cvcgl_bounds_walk.cpp @@ -4,9 +4,9 @@ // The walk ran, per root child: a dynamic_pointer_cast, a // dynamic_cast inside getCombinedBoundingBox, and a // vtkMatrix4x4 New + DeepCopy (getWorldTransform) to push 8 corners through. -// In the demo3 wasm profile one walk over ~110 nodes cost 0.6-1 ms in Firefox, -// ~27% of it the matrix copies and ~23% the casts. Now: flags instead of casts, -// and the node's cached world matrix (getCombinedWorldBoundingBox). +// In a wasm profile of a large scene the walk was a visible per-frame cost in +// Firefox, much of it the matrix copies and the casts. Now: flags instead of +// casts, and the node's cached world matrix (getCombinedWorldBoundingBox). // // Pins (headless): // A. the result is BIT-IDENTICAL to the old algorithm (replicated here) over diff --git a/src/cvcGL/test/cvcgl_camera_idle.cpp b/src/cvcGL/test/cvcgl_camera_idle.cpp index aeb9aea13..8cdb2f085 100644 --- a/src/cvcGL/test/cvcgl_camera_idle.cpp +++ b/src/cvcGL/test/cvcgl_camera_idle.cpp @@ -5,9 +5,10 @@ // update() used to call applyToCamera() every frame. Re-setting an unchanged // pose is a no-op for vtkCamera, but the vtkRenderer::ResetCameraClippingRange() // that comes with it re-derives every visible prop's bounds -- a mapper Update -// plus a cell-bounds pass for each mesh rewritten since the last frame. In the -// demo3 wasm profile that was 1-1.5 ms a frame (plus ~2 ms of throttled pose -// mirroring: 14 state writes, each a path lookup), with the camera standing still. +// plus a cell-bounds pass for each mesh rewritten since the last frame. In a wasm +// profile of a large scene with heavy per-frame overlays that was a measurable cost +// every frame (plus the throttled pose mirroring: 14 state writes, each a path +// lookup), with the camera standing still. // // Pins: // A. idle frames: no clipping-range reset, no camera change, no pose mirror; @@ -173,7 +174,7 @@ static void headless(cvc::app &app) { chk(resets.take() == 1, "a registered node moved: re-fitted"); chk(inSlab(vc, movedPt), "and the moved node lies inside [near, far]"); - // Not registered: a direct child of the graphics root (demo3's vehicle slots). + // Not registered: a direct child of the graphics root (e.g. an app's per-vehicle nodes). auto loose = sg.getGraphicsRoot()->addGraphicsChild("loose"); sg.processEvents(); cam.update(0.016); @@ -357,8 +358,8 @@ static void streamingAndLod(cvc::app &app) { } chk(resets.take() == 0, "re-pinning the same box every frame does not"); - // A streaming node the host keeps out of the clipping fit (UseBounds off, as - // demo3 does for its dynamic overlays) grows without re-fitting anything. + // A streaming node the host keeps out of the clipping fit (UseBounds off, as an + // app might for its dynamic overlays) grows without re-fitting anything. auto loose = sg.getGraphicsRoot()->addGraphicsChild( "loose", 64, 1.0f, cvc::bounding_box(0, 0, 0, 10, 10, 1)); loose->prop()->UseBoundsOff(); diff --git a/src/cvcGL/test/cvcgl_metadata_mirror.cpp b/src/cvcGL/test/cvcgl_metadata_mirror.cpp index 78f648a7d..59adfa9fb 100644 --- a/src/cvcGL/test/cvcgl_metadata_mirror.cpp +++ b/src/cvcGL/test/cvcgl_metadata_mirror.cpp @@ -5,7 +5,7 @@ // The mirror used to be locked against its own owner: after the first write // every later setMetadata threw read_only_error, which was swallowed. The tree // kept the FIRST value forever, and each setGeometry paid for ~17 exceptions -- -// about 40% of a wall re-mesh in the demo3 wasm profile. This pins: +// a large share of a mesh rebuild in a wasm profile. This pins: // A. the mirror follows the node: a second setGeometry updates it, and it // stays read-only to everyone else; // B. an unchanged value costs nothing: no state write, no signal; diff --git a/src/cvcGL/test/cvcgl_nav_stats_publish.cpp b/src/cvcGL/test/cvcgl_nav_stats_publish.cpp index d4c234216..cfb8570b7 100644 --- a/src/cvcGL/test/cvcgl_nav_stats_publish.cpp +++ b/src/cvcGL/test/cvcgl_nav_stats_publish.cpp @@ -1,7 +1,7 @@ // cvcgl_nav_stats_publish — publish_nav_stats writes a base episode into .nav_stats.* on // the value lane, and the values read back correctly after a flush. Pins the path layout + the -// per-convoy formation aggregate the ImGui panel / DSL will read (SPEC 1, CVC-DBG/cvcdbg -// stats-statetree spec). +// per-convoy formation aggregate the ImGui panel / DSL will read +// (docs/roadmap/NAV-STATS-INTRINSIC-ROADMAP.md, Track 3b-1). #undef NDEBUG #include #include diff --git a/src/cvcGL/test/cvcgl_shadow_casters.cpp b/src/cvcGL/test/cvcgl_shadow_casters.cpp index 229b015fc..96b2951b1 100644 --- a/src/cvcGL/test/cvcgl_shadow_casters.cpp +++ b/src/cvcGL/test/cvcgl_shadow_casters.cpp @@ -2,11 +2,12 @@ // out of the shadow maps, and -- the point -- out of the decision to re-bake. // // vtkShadowMapBakerPass re-renders every caster into its depth maps whenever ANY -// view prop's MTime moved, hidden or not, casting or not. In the demo3 wasm -// profile that was a 30-37 ms bake on every due frame, triggered by vehicle -// poses, a fog texture repaint and overlay restyles -- none of which change a -// building's shadow. The scene's baker now re-bakes only for a light, or a prop -// that casts, that changed (or joined or left), and draws casters only. +// view prop's MTime moved, hidden or not, casting or not. In a wasm profile of a +// large scene with heavy per-frame overlays that was an expensive bake on every due +// frame, triggered by vehicle poses, a fog texture repaint and overlay restyles -- +// none of which change a building's shadow. The scene's baker now re-bakes only for +// a light, or a prop that casts, that changed (or joined or left), and draws +// casters only. // // Pins: // A. (headless) the flag: default true, inherited by descendants -- including