feat: mount 3d app shell with camera rig
Add busPath curve helpers (pointAt/tangentAt), CameraRig as the sole consumer of useRide.frame(), and App.tsx wiring the R3F Canvas at low dpr for chunky pixels. Replace the boot.ts stub with the real React root mount and append fixed-canvas CSS.
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
||||
import { Canvas } from '@react-three/fiber'
|
||||
import { CameraRig } from './scene/CameraRig'
|
||||
import { useRideInput } from './ride/useRideInput'
|
||||
|
||||
export default function App() {
|
||||
useRideInput()
|
||||
return (
|
||||
<div id="demo">
|
||||
<Canvas
|
||||
dpr={0.6}
|
||||
gl={{ antialias: false }}
|
||||
camera={{ fov: 55, position: [0, 6.5, -11], near: 0.1, far: 900 }}
|
||||
>
|
||||
<color attach="background" args={['#05010d']} />
|
||||
<ambientLight intensity={0.55} />
|
||||
<directionalLight position={[60, 90, 30]} intensity={1.3} />
|
||||
<CameraRig />
|
||||
{/* World (Task 6), Bus (Task 7), Sky (Task 8),
|
||||
Passengers (Task 7), Stops (Task 9) mount here */}
|
||||
</Canvas>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+5
-1
@@ -1,3 +1,7 @@
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { createElement } from 'react'
|
||||
import App from './App'
|
||||
|
||||
export function boot(): void {
|
||||
console.log('demo boot placeholder — replaced by Task 5')
|
||||
createRoot(document.getElementById('root')!).render(createElement(App))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { useFrame } from '@react-three/fiber'
|
||||
import { Vector3 } from 'three'
|
||||
import { pointAt, tangentAt } from './path'
|
||||
import { useRide } from '../ride/store'
|
||||
|
||||
const eye = new Vector3()
|
||||
const look = new Vector3()
|
||||
|
||||
export function CameraRig() {
|
||||
useFrame((state, dt) => {
|
||||
useRide.getState().frame(Math.min(dt, 0.1))
|
||||
const t = useRide.getState().t
|
||||
const p = pointAt(t)
|
||||
const tan = tangentAt(t)
|
||||
eye.copy(p).addScaledVector(tan, -11)
|
||||
eye.y += 6.5
|
||||
eye.x += Math.sin(state.clock.elapsedTime * 0.4) * 0.6 // gentle sway
|
||||
look.copy(p).addScaledVector(tan, 6)
|
||||
state.camera.position.lerp(eye, Math.min(1, dt * 4))
|
||||
state.camera.lookAt(look)
|
||||
})
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { CatmullRomCurve3, Vector3 } from 'three'
|
||||
|
||||
const POINTS: Array<[number, number, number]> = [
|
||||
[0, 0, 0], [18, 0, -6], [34, 0, 4], [52, 0, -2],
|
||||
[70, 0, -14], [88, 0, -6], [104, 0, 6], [122, 0, 0],
|
||||
[140, 0, -10], [158, 0, -2], [176, 0, 8], [196, 0, 0],
|
||||
]
|
||||
|
||||
export const busPath = new CatmullRomCurve3(
|
||||
POINTS.map((p) => new Vector3(...p)),
|
||||
false,
|
||||
'catmullrom',
|
||||
0.3,
|
||||
)
|
||||
|
||||
export function pointAt(t: number): Vector3 {
|
||||
return busPath.getPointAt(Math.min(1, Math.max(0, t)))
|
||||
}
|
||||
|
||||
export function tangentAt(t: number): Vector3 {
|
||||
return busPath.getTangentAt(Math.min(1, Math.max(0, t)))
|
||||
}
|
||||
@@ -30,3 +30,6 @@ body.demo-on #fallback {
|
||||
clip-path: inset(50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#demo { position: fixed; inset: 0; }
|
||||
#demo canvas { image-rendering: pixelated; width: 100%; height: 100%; }
|
||||
|
||||
Reference in New Issue
Block a user