Novolis Docs
novolis-governance / completed-plans/platform_gaps_packrefs_ab8d5aa1.plan.md

Platform library gaps + dogfood PackRef migration

dotnetgovernancenovolis

name: Platform gaps PackRefs overview: Add the dogfood-driven platform types across Physics.Ballistics, Simulation (World/Builders/View), and Raylib.Game; bump local packages to 0.4.0-local; convert BouncingBall, DoomLite3D, and ArtillerySimulator to PackageReference-only consumers; refactor each app to use the new APIs. todos:

  • id: physics-ballistics-core

content: Add IHeightSampler, AxisAlignedRangeBox, ProjectileTerrainStepper, BallisticTrajectoryRunner, tests, INTEGRATION.md; bump physics to 0.4.0-local status: completed

  • id: simulation-heightfield-planar

content: Add BoundedHeightfield, HeightfieldMeshBuilder, PlanarOccupancy.TryRaycastDisc, tests; bump simulation to 0.4.0-local status: completed

  • id: simulation-view-cameras

content: Add ViewPose, OrbitCameraRig, FreeLookCameraRig, TrackingCameraRig + tests status: completed

  • id: raylib-diagnostics

content: Add DiagnosticsOverlay, SmoothedFps, optional DrawHeightfieldWires; bump raylib to 0.4.0-local status: completed

  • id: pack-and-versions

content: Run pack-novolis-local.ps1; update dogfooding Directory.Packages.props to 0.4.0-local status: completed

  • id: migrate-bouncingball

content: "BouncingBall: PackageReference-only csproj + DiagnosticsOverlay" status: completed

  • id: migrate-doomlite

content: "DoomLite3D: PackageReference-only + TryRaycastDisc + ViewPose bridge + DiagnosticsOverlay" status: completed

  • id: migrate-artillery

content: "ArtillerySimulator: PackageReference-only + library terrain/runner/cameras; delete ProjectileRun/BallisticArcPreview/ArtilleryCamera" status: completed

  • id: validate-dogfood

content: Release build slnx + manual run acceptance for all three apps status: completed isProject: false


Goal

Implement the framework types identified from BouncingBall, DoomLite3D, and ArtillerySimulator, ship them via existing NuGet packages (no new repos), bump `0.4.0-local`, pack to `artifacts/nuget-local`, and migrate the three apps to `PackageReference` only per design.md.

Constraints (governance)

  • Stack: Math → Physics → Simulation; Raylib must not reference Simulation (library-boundaries.md).
  • ViewPose lives in Simulation.View; GPU bridge stays a one-liner in apps: RayCamera.Perspective(pose.Position, pose.Target, pose.Up, pose.FieldOfView).
  • Procedural terrain styles (TerrainHeightSampler, TerrainStyle) stay in the Artillery app; libraries take IHeightSampler delegates.
flowchart TB
  subgraph physics [novolis-physics]
    IHeightSampler[IHeightSampler]
    Stepper[ProjectileTerrainStepper]
    Runner[BallisticTrajectoryRunner]
  end
  subgraph simulation [novolis-simulation]
  BHF[BoundedHeightfield]
  HFB[HeightfieldMeshBuilder]
  Orbit[OrbitCameraRig]
  Disc[PlanarOccupancy.TryRaycastDisc]
  end
  subgraph raylib [novolis-raylib]
  Diag[DiagnosticsOverlay]
  end
  AS[ArtillerySimulator]
  DL[DoomLite3D]
  BB[BouncingBall]
  IHeightSampler --> BHF
  BHF --> HFB
  Stepper --> Runner
  HFB --> AS
  Runner --> AS
  BHF --> AS
  Orbit --> AS
  Disc --> DL
  Diag --> BB
  Diag --> DL
  Diag --> AS

Phase 1 — Physics.Abstractions + Physics.Ballistics (P0)

Repo: novolis-physics

New types (Novolis.Physics.Abstractions)

TypeRole
IHeightSamplerfloat SampleHeight(float x, float z) for terrain contact
AxisAlignedRangeBoxXZ extent [0, extent] helpers used by bounded ballistics

New types (Novolis.Physics.Ballistics)

TypeRole
ProjectileTerrainImpactReasonTerrainMesh, Heightfield, BeyondRange, MaxSteps
ProjectileTerrainImpactPosition, velocity, time, horizontal range, reason
ProjectileTerrainStepOptionsdt, sphere radius, max sweep per substep, max steps, substep budget
ProjectileTerrainStepperCanonical integrate-then-sweep loop matching `ProjectileSemiImplicitIntegrator`: displacement = candidate.Position - startPos; sub-split when `disp> maxSweep; BallisticsQueries.SweepProjectileSphere`; heightfield + range-box checks
BallisticTrajectoryRunnerStateful runner: Begin / AdvanceWithBudget / trail buffer / Impact; shared by flight and preview
HeightfieldContactSegment height test + surface projection (replaces app-only TryHeightfieldContact / misuse of `GroundImpact` for terrain)
SegmentImpactInterpolatorLerp impact point/time/velocity along sweep hit (extract from test patterns in `CollisionSweepScenarioTests`)

Bounded range behavior (encode range-limit fix): when segment exits AxisAlignedRangeBox, impact position = project XZ to box edge + `IHeightSampler` Y (same semantics as completed fix_range-limit plan), not in-air Y.

Tests

  • New ProjectileTerrainStepperTests in Novolis.Physics.Unit: flat mesh hit, heightfield hit, range-box ground projection, large-step sub-split (parity with SweepLimitationScenarioTests).
  • Extend INTEGRATION.md §2 with “Terrain flight loop” section pointing at ProjectileTerrainStepper (not hand-rolled sweeps).

Version

Bump `Novolis.Physics.Packaging.props` → `0.4.0-local`.


Phase 2 — Simulation.World + World.Builders (P1)

Repo: novolis-simulation

Novolis.Simulation.World

TypeRole
BoundedHeightfieldWraps IHeightSampler + AxisAlignedRangeBox: IsInside, SampleHeight, TrySegmentLeavesRange, ProjectOntoSurface, TryHeightfieldContact (move logic from `TerrainWorld.cs`)
WorldExtentOptionsExtentMeters, collision grid resolution, draw decimation
PlanarOccupancy.TryRaycastDisc + PlanarDiscHitXZ ray vs columnar target (replaces `CombatRaycast`)

Novolis.Simulation.World.Builders

TypeRole
HeightfieldMeshBuilderHeight grid → StaticTriangleMesh + BvhStaticWorld (extract from TerrainWorld.BuildMeshes)
HeightfieldBuildResultCollision BVH + optional decimated draw vertices

Tests

  • BoundedHeightfieldTests, HeightfieldMeshBuilderTests in simulation test projects.
  • PlanarOccupancyDiscRaycastTests (hit/miss/max range).

Version

Bump `Novolis.Simulation.Packaging.props` → `0.4.0-local`.


Phase 3 — Simulation.View cameras (P2)

Repo: novolis-simulation

TypeRole
ViewPosePosition, Target, Up, FieldOfViewDegrees (platform observer output)
OrbitCameraRigTarget, distance, yaw/pitch, smoothing → ViewPose
FreeLookCameraRigWASD-style free position + yaw/pitch → ViewPose
TrackingCameraRigOptional: smooth follow moving target (shot trail / gun pivot)

Port behavior from `ArtilleryCamera.cs` into rigs; keep product FOV/speed constants in app.

Tests

  • OrbitCameraRigTests (snap, smooth, clamp pitch).

Phase 4 — Raylib.Game diagnostics (P4)

Repo: novolis-raylib

TypeRole
SmoothedFpsEMA FPS helper
FrameDiagnosticsWorking set, GC heap, optional custom lines
DiagnosticsOverlayF3 toggle, panel draw via RayGameContext

Replace duplicated `DiagnosticsHud.cs` / DoomLite copy with overlay + app-specific Action<FrameDiagnostics> lines.

Optional (same phase if cheap): RayGameContext.DrawHeightfieldWires(vertices, cellsX, cellsZ) to replace Artillery bolt loop in `TerrainWorld.Draw`.

Version

Bump `Novolis.Raylib.Packaging.props` → `0.4.0-local`.


Phase 5 — Pack and central versions

  1. Run `scripts/pack-novolis-local.ps1` (math/physics/simulation/raylib in dependency order).
  2. Update novolis-dogfooding/Directory.Packages.props: all Novolis 0.3.0-local → `0.4.0-local` (add any missing package IDs: Novolis.Physics.Ballistics already listed; ensure Novolis.Raylib pulls updated Game assembly).

Phase 6 — App migrations (PackageReference only)

Shared csproj pattern

Replace all ProjectReference to novolis-* with PackageReference (no version in csproj; central versions only). Keep app Program.cs + Game/ unchanged in structure.

AppPackageReferences
BouncingBall.csprojNovolis.Raylib, Novolis.Math.Arrays, Novolis.Simulation.World, Novolis.Simulation.World.Builders, Novolis.Physics.Collision.Simple
DoomLite3D.csprojNovolis.Raylib, Novolis.Math.Arrays, Novolis.Math.Geometry, Novolis.Simulation.World, Novolis.Simulation.View, Novolis.Simulation.Kinematics
ArtillerySimulator.csprojNovolis.Raylib, Novolis.Physics.Ballistics, Novolis.Physics.Collision.Simple, Novolis.Simulation.World, Novolis.Simulation.World.Builders, Novolis.Simulation.View

BouncingBall

  • Swap in DiagnosticsOverlay (custom lines: ball count, contacts, substeps from BallWorld stats).
  • No pile/spawn library work (optional SpawnSphereLayout deferred).

DoomLite3D

  • EnemySystem / weapon hit: use PlanarOccupancy.TryRaycastDisc; delete CombatRaycast.cs.
  • PlayerController.BuildRaylibCamera: build from YawPitchController → ViewPose → RayCamera.Perspective(...).
  • DiagnosticsOverlay with enemy/level seed lines.

ArtillerySimulator (largest refactor)

App fileBecomes
`TerrainWorld.cs`Thin wrapper: BoundedHeightfield + HeightfieldMeshBuilder + app TerrainHeightSampler adapter implementing IHeightSampler; draw uses optional DrawHeightfieldWires
`ProjectileRun.cs`Deleted — use BallisticTrajectoryRunner
`BallisticArcPreview.cs`Deleted — BallisticTrajectoryRunner preview mode (coarser dt / point cap)
`ArtilleryCamera.cs`Deleted — OrbitCameraRig + FreeLookCameraRig
`SimulationUnits.cs`Keep as app tuning; pass into WorldExtentOptions
`AtmosphereModel.cs`Unchanged (product preset over StandardAtmosphere)

`ArtillerySimulatorGame.cs` wires runner + rigs + overlay only.

Docs

  • Update design.md app table with new package columns.
  • Update README.md quick start: pack → restore → build (no project-ref path for these three apps).

Validation

d:\novolis\scripts\pack-novolis-local.ps1
cd d:\novolis\novolis-dogfooding
dotnet build Novolis.Dogfooding.slnx -c Release
dotnet run --project apps/BouncingBall -c Release --no-build
dotnet run --project apps/DoomLite3D -c Release --no-build
dotnet run --project apps/ArtillerySimulator -c Release --no-build

Artillery acceptance: flat terrain + vacuum ≈ textbook range; high-angle shot at map edge shows ground Y at edge (range-limit projection); preview arc matches flight endpoint; no regression in sweep hits on hills.

DoomLite acceptance: hitscan still kills enemies; F3 diagnostics; movement unchanged.

BouncingBall acceptance: spawn/stress (B / Ctrl+B) unchanged; F3 diagnostics.


Explicitly out of scope

  • New NuGet repos / “Kit” packages
  • DoomLite maze/enemy/boss systems (stay in app)
  • SpawnSphereLayout / dual-BVH documentation-only helpers (unless time remains in P4)
  • BridgeCommander / WireFishViewer csproj changes
  • Pushing 0.4.0-local to nuget.org (local feed only)