From 5d73943e584b1c0c0753dda85fbda34d27726986 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 21 Sep 2026 23:47:42 +0800 Subject: feat(ship): HNX-02 Copernicus rework — continuous hull, few-large-element scale, Draco pipeline, side-on framing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SomhairlesDream.Frontend/App.fs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'src/SomhairlesDream.Frontend/App.fs') diff --git a/src/SomhairlesDream.Frontend/App.fs b/src/SomhairlesDream.Frontend/App.fs index 82bae88..72d1be2 100644 --- a/src/SomhairlesDream.Frontend/App.fs +++ b/src/SomhairlesDream.Frontend/App.fs @@ -184,25 +184,33 @@ let frameObject (view: ViewState) (node: ThreeNode) = let size = createVector3 () box.getCenter center |> ignore box.getSize size |> ignore - let radius = 0.5 * size.length () + // Fit the longest axis horizontally (it runs across the screen in the + // side-on hero camera) and the next-longest vertically. + let axes = [| size.x; size.y; size.z |] |> Array.sort + let longest = axes.[2] + let smallest = axes.[0] - if radius > 0.000001 then + if longest > 0.000001 then node.position.set(-center.x, -center.y, -center.z) |> ignore let camera = view.Camera let width = max 1.0 (float view.Canvas.clientWidth) let height = max 1.0 (float view.Canvas.clientHeight) - let aspect = width / height + let aspect = max 0.3 (width / height) camera.aspect <- aspect let fovY = camera.fov * Math.PI / 180.0 - let fovX = 2.0 * atan (tan (fovY / 2.0) * aspect) - let limitingFov = min fovY fovX - let distance = 1.28 * radius / sin (limitingFov / 2.0) - let dirX, dirY, dirZ = 0.62, 0.45, 0.70 + let tanHalf = tan (fovY / 2.0) + // Longest axis lies across the screen; the smallest axis is the + // one stacking vertically in a side-on view of a slender vehicle. + let halfW = 0.5 * longest / (tanHalf * aspect) + let halfH = 0.5 * smallest / tanHalf + let distance = 1.25 * max halfW halfH + // Side-on hero camera: look across the hull, not down its nose. + let dirX, dirY, dirZ = 0.30, 0.44, 0.85 let norm = sqrt (dirX * dirX + dirY * dirY + dirZ * dirZ) camera.position.set(dirX / norm * distance, dirY / norm * distance, dirZ / norm * distance) |> ignore - camera.near <- max (distance * 0.01) (distance - radius * 2.5) - camera.far <- distance + radius * 4.0 + camera.near <- max 1.0 (distance * 0.05) + camera.far <- distance + longest * 3.0 camera.lookAt(0.0, 0.0, 0.0) camera.updateProjectionMatrix () @@ -374,6 +382,10 @@ let startStepLoad (view: ViewState) (owner: LoadOwner) (historical: bool) = loadedStep <- None toggleHidden ("viewport-loading", false) let loader = createGLTFLoader () + // Attach the Draco decoder so geometry-compressed GLBs stream in small. + let draco = createDRACOLoader () + setDracoDecoderPath draco "/vendor/addons/libs/draco/" + attachDracoLoader loader draco let projectId = JS.encodeURIComponent owner.ProjectId let runId = JS.encodeURIComponent owner.RunId // Cache-buster: the GLB is always re-fetched from the server. @@ -723,7 +735,7 @@ let probeSelection : RunSelection = let shipSelection : RunSelection = { ProjectId = "ship-001" - RunId = "run-ship-001" + RunId = "run-ship-002" BaseId = "ship-base-000" TargetId = "ship-target-007" Render = false } -- cgit v1.2.3