diff options
Diffstat (limited to 'src/SomhairlesDream.Frontend')
| -rw-r--r-- | src/SomhairlesDream.Frontend/App.fs | 32 | ||||
| -rw-r--r-- | src/SomhairlesDream.Frontend/Bindings.fs | 9 |
2 files changed, 31 insertions, 10 deletions
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 } diff --git a/src/SomhairlesDream.Frontend/Bindings.fs b/src/SomhairlesDream.Frontend/Bindings.fs index 7f0ce31..05d8e95 100644 --- a/src/SomhairlesDream.Frontend/Bindings.fs +++ b/src/SomhairlesDream.Frontend/Bindings.fs @@ -59,6 +59,15 @@ type ServerEvent = type GLTFScene = abstract scene: ThreeNode with get +[<Emit("new DRACOLoader()")>] +let createDRACOLoader () : obj = jsNative + +[<Emit("$0.setDecoderPath($1)")>] +let setDracoDecoderPath (draco: obj) (path: string) : unit = jsNative + +[<Emit("$0.setDRACOLoader($1)")>] +let attachDracoLoader (loader: obj) (draco: obj) : unit = jsNative + [<Emit("new GLTFLoader()")>] let createGLTFLoader () : obj = jsNative |
