summaryrefslogtreecommitdiff
path: root/src/SomhairlesDream.Frontend
diff options
context:
space:
mode:
Diffstat (limited to 'src/SomhairlesDream.Frontend')
-rw-r--r--src/SomhairlesDream.Frontend/App.fs32
-rw-r--r--src/SomhairlesDream.Frontend/Bindings.fs9
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