update logistices
This commit is contained in:
125
src/components/logisticsbrain/LogisticsBrainCanvas.tsx
Normal file
125
src/components/logisticsbrain/LogisticsBrainCanvas.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
"use client";
|
||||
|
||||
import React, { useRef } from "react";
|
||||
import { Canvas, useFrame } from "@react-three/fiber";
|
||||
import { EffectComposer, Bloom, Vignette } from "@react-three/postprocessing";
|
||||
import { KernelSize } from "postprocessing";
|
||||
import * as THREE from "three";
|
||||
import { C, WAYPOINTS } from "./theme";
|
||||
import { clamp01, damp, lerp, smoothstep } from "./math";
|
||||
import Brain from "./Brain";
|
||||
import City from "./City";
|
||||
import Routes from "./Routes";
|
||||
import Network from "./Network";
|
||||
import SLAClock from "./SLAClock";
|
||||
|
||||
type Props = {
|
||||
progress: React.RefObject<number>;
|
||||
reduced?: boolean;
|
||||
isMobile?: boolean;
|
||||
active?: boolean;
|
||||
};
|
||||
|
||||
const posTarget = new THREE.Vector3();
|
||||
const lookTarget = new THREE.Vector3();
|
||||
|
||||
/**
|
||||
* Cinematic camera that flies along the waypoint spline by scroll progress.
|
||||
* Adjacent waypoints are smoothstep-interpolated, then the camera exponentially
|
||||
* damps toward the result so even fast/flung scrolls glide instead of snapping.
|
||||
*/
|
||||
function CameraRig({ progress }: { progress: React.RefObject<number> }) {
|
||||
const lookCurrent = useRef(new THREE.Vector3(0, 6, 0));
|
||||
const inited = useRef(false);
|
||||
|
||||
useFrame((state, dt) => {
|
||||
const p = clamp01(progress.current ?? 0);
|
||||
|
||||
// Locate the active waypoint segment.
|
||||
let i = 0;
|
||||
for (let k = 0; k < WAYPOINTS.length - 1; k++) {
|
||||
if (p >= WAYPOINTS[k].at && p <= WAYPOINTS[k + 1].at) { i = k; break; }
|
||||
if (p > WAYPOINTS[WAYPOINTS.length - 1].at) i = WAYPOINTS.length - 2;
|
||||
}
|
||||
const a = WAYPOINTS[i];
|
||||
const b = WAYPOINTS[i + 1];
|
||||
const span = b.at - a.at || 1;
|
||||
const lt = smoothstep(0, 1, clamp01((p - a.at) / span));
|
||||
|
||||
posTarget.set(
|
||||
lerp(a.pos[0], b.pos[0], lt),
|
||||
lerp(a.pos[1], b.pos[1], lt),
|
||||
lerp(a.pos[2], b.pos[2], lt),
|
||||
);
|
||||
lookTarget.set(
|
||||
lerp(a.look[0], b.look[0], lt),
|
||||
lerp(a.look[1], b.look[1], lt),
|
||||
lerp(a.look[2], b.look[2], lt),
|
||||
);
|
||||
|
||||
// Very subtle idle breathing so the shot isn't dead-static — kept tiny (~70%
|
||||
// smaller than before) so the camera reads as a fixed map view, not a fly-through.
|
||||
const t = state.clock.elapsedTime;
|
||||
posTarget.x += Math.sin(t * 0.16) * 0.14;
|
||||
posTarget.y += Math.sin(t * 0.21) * 0.07;
|
||||
|
||||
const cam = state.camera;
|
||||
if (!inited.current) {
|
||||
cam.position.copy(posTarget);
|
||||
lookCurrent.current.copy(lookTarget);
|
||||
inited.current = true;
|
||||
} else {
|
||||
cam.position.x = damp(cam.position.x, posTarget.x, 2.6, dt);
|
||||
cam.position.y = damp(cam.position.y, posTarget.y, 2.6, dt);
|
||||
cam.position.z = damp(cam.position.z, posTarget.z, 2.6, dt);
|
||||
lookCurrent.current.x = damp(lookCurrent.current.x, lookTarget.x, 3, dt);
|
||||
lookCurrent.current.y = damp(lookCurrent.current.y, lookTarget.y, 3, dt);
|
||||
lookCurrent.current.z = damp(lookCurrent.current.z, lookTarget.z, 3, dt);
|
||||
}
|
||||
cam.lookAt(lookCurrent.current);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
function LogisticsBrainCanvas({ progress, reduced = false, isMobile = false, active = true }: Props) {
|
||||
return (
|
||||
<Canvas
|
||||
flat
|
||||
dpr={[1, isMobile || reduced ? 1.3 : 1.7]}
|
||||
camera={{ position: WAYPOINTS[0].pos, fov: 52, near: 0.1, far: 200 }}
|
||||
gl={{ antialias: !isMobile, powerPreference: "high-performance", alpha: false }}
|
||||
frameloop={active ? "always" : "never"}
|
||||
>
|
||||
<color attach="background" args={[C.bg]} />
|
||||
<fog attach="fog" args={[C.bg, 40, 100]} />
|
||||
|
||||
<ambientLight intensity={0.55} />
|
||||
<directionalLight position={[8, 18, 10]} intensity={0.7} color={C.sky} />
|
||||
<pointLight position={[-10, 8, -8]} intensity={40} distance={60} color={C.purple} />
|
||||
<pointLight position={[0, 9, 0]} intensity={30} distance={40} color={C.cyan} />
|
||||
|
||||
<CameraRig progress={progress} />
|
||||
<City progress={progress} reduced={reduced} isMobile={isMobile} />
|
||||
<Routes progress={progress} reduced={reduced} isMobile={isMobile} />
|
||||
<Network progress={progress} reduced={reduced} isMobile={isMobile} />
|
||||
<SLAClock progress={progress} reduced={reduced} isMobile={isMobile} />
|
||||
<Brain progress={progress} reduced={reduced} isMobile={isMobile} />
|
||||
|
||||
{!reduced && (
|
||||
<EffectComposer multisampling={isMobile ? 0 : 2}>
|
||||
<Bloom
|
||||
mipmapBlur
|
||||
intensity={isMobile ? 0.9 : 1.25}
|
||||
luminanceThreshold={0.12}
|
||||
luminanceSmoothing={0.045}
|
||||
radius={isMobile ? 0.65 : 0.82}
|
||||
kernelSize={KernelSize.MEDIUM}
|
||||
/>
|
||||
<Vignette eskil={false} offset={0.22} darkness={0.62} />
|
||||
</EffectComposer>
|
||||
)}
|
||||
</Canvas>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(LogisticsBrainCanvas);
|
||||
Reference in New Issue
Block a user