diff --git a/src/components/AtomGlitch.tsx b/src/components/AtomGlitch.tsx index 2af4d91..c8c25d9 100644 --- a/src/components/AtomGlitch.tsx +++ b/src/components/AtomGlitch.tsx @@ -290,7 +290,7 @@ function CircularText({ text, radius, speed, tilt, fontSize, color, emissive, op function CountdownRing() { const [time, setTime] = useState(''); const groupRef = useRef(null); - const radius = 4.2; + const radius = 2.5; useEffect(() => { function tick() { @@ -353,50 +353,50 @@ function CountdownRing() { function CountdownText() { return ( - {/* "L'electron" — large ring, slow spin */} + {/* "L'electron" — tight elliptical orbit, close to nucleus */} - {/* "rare" — opposite tilt, bigger */} + {/* "rare" — opposite tilt, close */} - {/* Subtitle — wider ring, slow */} + {/* Subtitle — medium ring */} - {/* "LANCEMENT DANS" — small ring */} + {/* "LANCEMENT DANS" — small tight ring */} createTraceCurve(), []); - const tubeGeo = useMemo(() => { - return new THREE.TubeGeometry(curve, 200, 0.08, 8, false); - }, [curve]); + if (!glb?.scene) return null; return ( - - {/* Main trace — copper tube */} - - - - {/* Glow around trace */} - - - - + ); } -/* ---------- Secondary traces (decorative, branch off main) ---------- */ -function SecondaryTraces() { - const traces = useMemo(() => { - const paths: THREE.Vector3[][] = []; - // Branch traces at various points along the main route - const branches = [ - // Hero area branches - [new THREE.Vector3(5, 0, -8), new THREE.Vector3(5, 0, -4), new THREE.Vector3(8, 0, -2)], - [new THREE.Vector3(7, 0, -11), new THREE.Vector3(10, 0, -7), new THREE.Vector3(13, 0, -6)], - // About area - [new THREE.Vector3(3, 0, -28), new THREE.Vector3(3, 0, -24), new THREE.Vector3(6, 0, -22)], - [new THREE.Vector3(-2, 0, -31), new THREE.Vector3(-5, 0, -27), new THREE.Vector3(-7, 0, -26)], - // Cases area - [new THREE.Vector3(-5, -0.1, -40), new THREE.Vector3(-3, -0.1, -36), new THREE.Vector3(0, -0.1, -35)], - [new THREE.Vector3(-8, -0.1, -46), new THREE.Vector3(-11, -0.1, -44), new THREE.Vector3(-13, -0.1, -42)], - // Sprints area - [new THREE.Vector3(6, 0, -65), new THREE.Vector3(6, 0, -61), new THREE.Vector3(9, 0, -59)], - [new THREE.Vector3(2, 0, -67), new THREE.Vector3(-1, 0, -63), new THREE.Vector3(-4, 0, -62)], - // Contact area - [new THREE.Vector3(5, 0, -80), new THREE.Vector3(8, 0, -76), new THREE.Vector3(10, 0, -75)], - [new THREE.Vector3(1, 0, -85), new THREE.Vector3(-2, 0, -82), new THREE.Vector3(-5, 0, -81)], - ]; - - branches.forEach(pts => { - const curve = new THREE.CatmullRomCurve3(pts, false, 'catmullrom', 0.5); - const tubePts = curve.getPoints(30); - paths.push(tubePts); - }); - return paths; - }, []); - - return ( - - {traces.map((pts, i) => { - const geo = new THREE.BufferGeometry().setFromPoints(pts); - return ( - - - - ); - })} - - ); -} - -/* ---------- Current flow particles along the trace ---------- */ -function CurrentFlow({ count = 60 }) { - const ref = useRef(null); - const dummy = useMemo(() => new THREE.Object3D(), []); - const curve = useMemo(() => createTraceCurve(), []); - const phases = useMemo(() => Array.from({ length: count }, () => Math.random()), [count]); - - useFrame(({ clock }) => { - if (!ref.current) return; - const t = clock.getElapsedTime(); - - for (let i = 0; i < count; i++) { - // Each particle flows along the curve - const progress = ((phases[i] + t * 0.06) % 1); - const pos = curve.getPointAt(progress); - - // Add slight jitter - dummy.position.set( - pos.x + (Math.random() - 0.5) * 0.05, - pos.y + 0.02 + Math.sin(t * 3 + i) * 0.02, - pos.z + (Math.random() - 0.5) * 0.05, - ); - - // Size based on proximity to camera/scroll position - const distToScroll = Math.abs(progress - scrollProgress); - const brightness = Math.max(0.2, 1 - distToScroll * 3); - dummy.scale.setScalar(0.03 * brightness + 0.01); - - dummy.updateMatrix(); - ref.current.setMatrixAt(i, dummy.matrix); - } - ref.current.instanceMatrix.needsUpdate = true; - }); - - return ( - - - - - ); -} - -/* ---------- Warm current (second layer) ---------- */ -function WarmCurrentFlow({ count = 30 }) { - const ref = useRef(null); - const dummy = useMemo(() => new THREE.Object3D(), []); - const curve = useMemo(() => createTraceCurve(), []); - const phases = useMemo(() => Array.from({ length: count }, () => Math.random()), [count]); - - useFrame(({ clock }) => { - if (!ref.current) return; - const t = clock.getElapsedTime(); - - for (let i = 0; i < count; i++) { - const progress = ((phases[i] + t * 0.04 + 0.5) % 1); - const pos = curve.getPointAt(progress); - dummy.position.set(pos.x, pos.y + 0.03, pos.z); - const distToScroll = Math.abs(progress - scrollProgress); - const brightness = Math.max(0.1, 1 - distToScroll * 4); - dummy.scale.setScalar(0.02 * brightness + 0.005); - dummy.updateMatrix(); - ref.current.setMatrixAt(i, dummy.matrix); - } - ref.current.instanceMatrix.needsUpdate = true; - }); - - return ( - - - - - ); -} - -/* ---------- PCB Substrate (green board) ---------- */ -function Substrate() { - return ( - - {/* Main board */} - - - - - {/* Solder mask grid pattern */} - {Array.from({ length: 20 }).map((_, i) => ( - - - - - ))} - {Array.from({ length: 14 }).map((_, i) => ( - - - - - ))} - - ); -} - -/* ---------- Vias (through-hole connections) ---------- */ -function Vias() { - const positions: [number, number, number][] = [ - [10, 0, -14], [8, 0, -18], [3, 0, -28], - [-3, 0, -36], [-7, 0, -42], [-6, 0, -50], - [0, 0, -60], [5, 0, -68], [3, 0, -80], - // Extra decorative vias - [12, 0, -10], [-9, 0, -28], [9, 0, -52], - [-10, 0, -65], [10, 0, -82], [-3, 0, -88], - ]; - - return ( - - {positions.map(([x, y, z], i) => ( - - {/* Via ring */} - - - - - {/* Via hole */} - - - - - - ))} - - ); -} - -/* ---------- 3D Electronic components on PCB ---------- */ - -/* ---------- GLB Model loaders ---------- */ - -/* IC — uses SOIC-8 or QFP-32 GLB model */ -function ICComponent({ position, size = [2, 0.3, 1.2], label, color = '#1a1a1a' }: { - position: [number, number, number]; size?: [number, number, number]; label: string; color?: string; +/* ---------- GLB Component placement ---------- */ +function GLBComponent({ modelUrl, position, scale = 1, rotation = [0, 0, 0] as [number, number, number] }: { + modelUrl: string; position: [number, number, number]; scale?: number; rotation?: [number, number, number]; }) { - const groupRef = useRef(null); - const isLarge = size[0] > 1.5; - const modelUrl = isLarge ? '/assets/models3d/qfp32.glb' : '/assets/models3d/soic8.glb'; - let glb: any = null; try { glb = useGLTF(modelUrl); } catch {} - - useFrame(({ clock }) => { - if (!groupRef.current) return; - const cameraZ = -scrollProgress * 90; - const dist = Math.abs(position[2] - cameraZ); - const glow = Math.max(0, 1 - dist / 6); - groupRef.current.position.y = position[1] + glow * Math.sin(clock.getElapsedTime() * 2) * 0.03; - }); - - const scale = isLarge ? 0.4 : 0.25; - + if (!glb?.scene) return null; return ( - - {glb?.scene ? ( - - ) : ( - - - - - )} - - {label} - - - - - - - ); -} - -/* Capacitor — uses capacitor_0805 GLB */ -function Capacitor({ position, radius = 0.25, height = 0.5, color = '#2a4a8a' }: { - position: [number, number, number]; radius?: number; height?: number; color?: string; -}) { - const ref = useRef(null); - let glb: any = null; - try { glb = useGLTF('/assets/models3d/capacitor_0805.glb'); } catch {} - - useFrame(() => { - if (!ref.current) return; - const cameraZ = -scrollProgress * 90; - const dist = Math.abs(position[2] - cameraZ); - const glow = Math.max(0, 1 - dist / 6); - ref.current.children.forEach(child => { - if ((child as any).material) { - (child as any).material.emissiveIntensity = glow * 0.5; - } - }); - }); - - const scale = radius * 4; - - return ( - - {glb?.scene ? ( - - ) : ( - - - - - )} - - ); -} - -/* Resistor — uses resistor_0603 GLB */ -function Resistor({ position, rotation = [0, 0, 0] }: { position: [number, number, number]; rotation?: [number, number, number] }) { - let glb: any = null; - try { glb = useGLTF('/assets/models3d/resistor_0603.glb'); } catch {} - - return ( - - {glb?.scene ? ( - - ) : ( - <> - - - - - - - - - - - - - - )} - - ); -} - -/* LED — uses led_0603 GLB + glow */ -function LED({ position, color = '#5bd1d8' }: { position: [number, number, number]; color?: string }) { - const ref = useRef(null); - const threeColor = useMemo(() => new THREE.Color(color), [color]); - let glb: any = null; - try { glb = useGLTF('/assets/models3d/led_0603.glb'); } catch {} - - useFrame(({ clock }) => { - if (!ref.current) return; - const t = clock.getElapsedTime(); - const cameraZ = -scrollProgress * 90; - const dist = Math.abs(position[2] - cameraZ); - const proximity = Math.max(0, 1 - dist / 5); - // Glow point light near LED - const light = ref.current.children.find(c => c.type === 'PointLight') as THREE.PointLight; - if (light) light.intensity = proximity * (0.3 + Math.sin(t * 3) * 0.1); - }); - - return ( - - {glb?.scene ? ( - - ) : ( - - - - - )} - - - ); -} - -/* Preload all GLB models */ -useGLTF.preload('/assets/models3d/resistor_0603.glb'); -useGLTF.preload('/assets/models3d/capacitor_0805.glb'); -useGLTF.preload('/assets/models3d/led_0603.glb'); -useGLTF.preload('/assets/models3d/soic8.glb'); -useGLTF.preload('/assets/models3d/qfp32.glb'); - -/* Silkscreen text (PCB printed labels) */ -function Silk({ position, text, size = 0.15, color = '#e8e8d0', opacity = 0.5, rotation = [-Math.PI / 2, 0, 0] as [number, number, number] }: { - position: [number, number, number]; text: string; size?: number; color?: string; opacity?: number; rotation?: [number, number, number]; -}) { - return ( - - {text} - + /> ); } -/* Section title — larger, glows when camera is near */ -function SectionLabel({ position, text, subtitle }: { position: [number, number, number]; text: string; subtitle?: string }) { +/* ---------- Section label (silkscreen on board) ---------- */ +function SectionLabel({ position, text, subtitle }: { + position: [number, number, number]; text: string; subtitle?: string; +}) { const ref = useRef(null); useFrame(() => { if (!ref.current) return; - const cameraZ = -scrollProgress * 90; - const dist = Math.abs(position[2] - cameraZ); - const glow = Math.max(0, 1 - dist / 6); - ref.current.children.forEach(child => { - if ((child as any).material) { - (child as any).material.opacity = 0.15 + glow * 0.7; - } - }); + // Glow based on camera proximity + const camPos = new THREE.Vector3(); + ref.current.getWorldPosition(camPos); }); return ( - + {text} {subtitle && ( {subtitle} @@ -529,205 +109,85 @@ function SectionLabel({ position, text, subtitle }: { position: [number, number, ); } -/* All components assembled on the PCB */ -function PCBComponents() { +/* ---------- Current flow particles ---------- */ +function CurrentFlow({ count = 80 }) { + const ref = useRef(null); + const dummy = useMemo(() => new THREE.Object3D(), []); + + // Particles follow a circular path around the PCB + const phases = useMemo(() => Array.from({ length: count }, () => Math.random()), [count]); + const radii = useMemo(() => Array.from({ length: count }, () => 3 + Math.random() * 8), [count]); + const heights = useMemo(() => Array.from({ length: count }, () => 0.3 + Math.random() * 0.5), [count]); + + useFrame(({ clock }) => { + if (!ref.current) return; + const t = clock.getElapsedTime(); + + for (let i = 0; i < count; i++) { + const angle = phases[i] * Math.PI * 2 + t * (0.1 + phases[i] * 0.1); + const r = radii[i]; + dummy.position.set( + Math.cos(angle) * r, + heights[i] + Math.sin(t * 2 + i) * 0.1, + Math.sin(angle) * r * 0.6, + ); + const proximity = Math.max(0.2, 1 - Math.abs(scrollProgress - phases[i]) * 3); + dummy.scale.setScalar(0.04 * proximity); + dummy.updateMatrix(); + ref.current.setMatrixAt(i, dummy.matrix); + } + ref.current.instanceMatrix.needsUpdate = true; + }); + return ( - - {/* ── HERO — Main MCU ── */} - - - - - - - - - - - - {/* ── ABOUT — Approche & expertise ── */} - - - - - - - - - - - - - - {/* ── CASES — Cas concrets ── */} - - - - - - - - - - - - {/* ── MEDIA — Photos & videos ── */} - - - - - - - - {/* ── SPRINTS — Missions ── */} - - - - - - - - - - - - {/* ── CONTACT — Energy storage ── */} - - - - - - - - - - - - - {/* ── Transition components between sections (~50 total) ── */} - - {/* ── HERO → ABOUT : STM32F030 + passives ── */} - - - - - - - - - - - - - - - - - - {/* ── ABOUT → CASES : 74HC595 + self + résistances ── */} - - - {/* Self (inductor) — cylindre avec bandes */} - - - - - - - - - - - {/* Self 2 */} - - - - {/* ── CASES → MEDIA : FPGA Xilinx + passives ── */} - - - - - - - - - - - - - - - - {/* ── MEDIA → SPRINTS : ARM Neural Unit + découplage ── */} - - - - - - - - - - - - - {/* Self */} - - - - {/* ── SPRINTS → CONTACT : DSP + condensateurs + LED ── */} - - - - - - - - - - - - - - - - - {/* Self */} - - - - {/* ── Extra scattered passives ── */} - - - - - - - - - - - - - {/* ── Board title silkscreen ── */} - - - - - + + + + ); } -/* ---------- Ambient floating particles (dust/solder flux) ---------- */ -function AmbientDust({ count = 200 }) { - const ref = useRef(null); +/* ---------- Warm current (second layer) ---------- */ +function WarmCurrent({ count = 40 }) { + const ref = useRef(null); + const dummy = useMemo(() => new THREE.Object3D(), []); + const phases = useMemo(() => Array.from({ length: count }, () => Math.random()), [count]); + useFrame(({ clock }) => { + if (!ref.current) return; + const t = clock.getElapsedTime(); + for (let i = 0; i < count; i++) { + const angle = phases[i] * Math.PI * 2 + t * 0.06 + Math.PI; + const r = 4 + phases[i] * 6; + dummy.position.set( + Math.cos(angle) * r, + 0.2 + Math.sin(t + i) * 0.05, + Math.sin(angle) * r * 0.5, + ); + dummy.scale.setScalar(0.025); + dummy.updateMatrix(); + ref.current.setMatrixAt(i, dummy.matrix); + } + ref.current.instanceMatrix.needsUpdate = true; + }); + + return ( + + + + + ); +} + +/* ---------- Ambient dust ---------- */ +function AmbientDust({ count = 150 }) { + const ref = useRef(null); const positions = useMemo(() => { const pos = new Float32Array(count * 3); for (let i = 0; i < count; i++) { - pos[i * 3] = (Math.random() - 0.5) * 25; - pos[i * 3 + 1] = Math.random() * 3; - pos[i * 3 + 2] = Math.random() * -42; + pos[i * 3] = (Math.random() - 0.5) * 30; + pos[i * 3 + 1] = Math.random() * 4; + pos[i * 3 + 2] = (Math.random() - 0.5) * 20; } return pos; }, [count]); @@ -737,7 +197,7 @@ function AmbientDust({ count = 200 }) { const arr = (ref.current.geometry.attributes.position as THREE.BufferAttribute).array as Float32Array; const t = clock.getElapsedTime(); for (let i = 0; i < count; i++) { - arr[i * 3 + 1] += Math.sin(t * 0.5 + i) * 0.001; + arr[i * 3 + 1] += Math.sin(t * 0.3 + i * 0.5) * 0.001; } ref.current.geometry.attributes.position.needsUpdate = true; }); @@ -747,82 +207,165 @@ function AmbientDust({ count = 200 }) { - + ); } -/* ---------- Camera with zoom stops at each component ---------- */ +/* ---------- Component clusters around the PCB ---------- */ +function ComponentCluster() { + const R = [-Math.PI / 2, 0, 0] as [number, number, number]; + const S_RES = 8; + const S_CAP = 10; + const S_LED = 8; + const S_IC_SM = 6; + const S_IC_LG = 3; -// Define zoom stops — camera zooms in when scroll reaches each section -const ZOOM_STOPS = [ - { scroll: 0.00, pos: [0, 6, 4], look: [3, 0, -8], fov: 55 }, // Overview start - { scroll: 0.08, pos: [10, 2.5, -10], look: [10, 0, -14], fov: 38 }, // HERO — zoom on ESP32 - { scroll: 0.22, pos: [0, 2.2, -28], look: [0, 0, -32], fov: 40 }, // ABOUT — zoom on LM358 - { scroll: 0.36, pos: [-7, 2.5, -40], look: [-8, 0, -44], fov: 38 }, // CASES — zoom on MOSFET - { scroll: 0.48, pos: [-4, 2, -52], look: [-4, 0, -56], fov: 40 }, // MEDIA — zoom on USB-C - { scroll: 0.62, pos: [5, 2.2, -64], look: [6, 0, -68], fov: 40 }, // SPRINTS — zoom on 7805 - { scroll: 0.78, pos: [2, 2.5, -80], look: [2, 0, -84], fov: 42 }, // CONTACT — zoom on caps - { scroll: 1.00, pos: [0, 8, -45], look: [0, 0, -45], fov: 60 }, // End — pull out overview + return ( + + {/* Zone 1 — MCU area (top-right of board) */} + + + + + + + + + + + {/* Zone 2 — Analog / Op-amp area (left of board) */} + + + + + + + + + + + {/* Zone 3 — Power stage (bottom-right) */} + + + + + + + + + + + {/* Zone 4 — Communication (bottom-left) */} + + + + + + + + + + {/* Zone 5 — DSP / Neural (center-top) */} + + + + + + + + + {/* Zone 6 — Contact area (center) */} + + + {/* Scattered passives */} + {Array.from({ length: 15 }).map((_, i) => ( + + ))} + {Array.from({ length: 10 }).map((_, i) => ( + + ))} + + ); +} + +/* ---------- Board title silkscreen ---------- */ +function BoardTitle() { + return ( + + + L'ELECTRON RARE + + + REV 2026.1 — BMU v2 — Made in France + + + lelectronrare.fr + + + ); +} + +/* ---------- Camera — orbits around PCB, zooms on scroll ---------- */ +const CAMERA_STOPS = [ + { scroll: 0.00, pos: [0, 15, 12], look: [0, 0, 0] }, // Overview — full board + { scroll: 0.12, pos: [7, 4, -2], look: [5, 0, -2] }, // MCU zone + { scroll: 0.28, pos: [-6, 4, -1], look: [-5, 0, -1] }, // Analog zone + { scroll: 0.42, pos: [5, 4, 5], look: [4, 0, 4] }, // Power zone + { scroll: 0.56, pos: [-6, 4, 5], look: [-5, 0, 4] }, // COM zone + { scroll: 0.70, pos: [0, 5, -6], look: [0, 0, -4] }, // Missions zone + { scroll: 0.85, pos: [0, 3, 2], look: [0, 0, 1] }, // Contact zone (close) + { scroll: 1.00, pos: [0, 18, 10], look: [0, 0, 0] }, // Pull back overview ]; -function TraceCamera() { +function OrbitCamera() { const { camera } = useThree(); - const smooth = useRef({ x: 0, y: 0, z: 0, lx: 0, ly: 0, lz: 0, fov: 50 }); + const smooth = useRef({ x: 0, y: 0, z: 0, lx: 0, ly: 0, lz: 0 }); useFrame(({ pointer }) => { const s = scrollProgress; - // Find the two nearest zoom stops - let a = ZOOM_STOPS[0], b = ZOOM_STOPS[1]; - for (let i = 0; i < ZOOM_STOPS.length - 1; i++) { - if (s >= ZOOM_STOPS[i].scroll && s <= ZOOM_STOPS[i + 1].scroll) { - a = ZOOM_STOPS[i]; - b = ZOOM_STOPS[i + 1]; + let a = CAMERA_STOPS[0], b = CAMERA_STOPS[1]; + for (let i = 0; i < CAMERA_STOPS.length - 1; i++) { + if (s >= CAMERA_STOPS[i].scroll && s <= CAMERA_STOPS[i + 1].scroll) { + a = CAMERA_STOPS[i]; + b = CAMERA_STOPS[i + 1]; break; } } - // Interpolation factor between stops const range = b.scroll - a.scroll; const t = range > 0 ? (s - a.scroll) / range : 0; - // Smooth easing - const ease = t * t * (3 - 2 * t); // smoothstep + const ease = t * t * (3 - 2 * t); - // Interpolate position - const tx = a.pos[0] + (b.pos[0] - a.pos[0]) * ease + pointer.x * 0.8; - const ty = a.pos[1] + (b.pos[1] - a.pos[1]) * ease + pointer.y * 0.3; + const tx = a.pos[0] + (b.pos[0] - a.pos[0]) * ease + pointer.x * 1.5; + const ty = a.pos[1] + (b.pos[1] - a.pos[1]) * ease + pointer.y * 0.5; const tz = a.pos[2] + (b.pos[2] - a.pos[2]) * ease; - - // Interpolate lookAt - const lx = a.look[0] + (b.look[0] - a.look[0]) * ease + pointer.x * 0.2; + const lx = a.look[0] + (b.look[0] - a.look[0]) * ease + pointer.x * 0.3; const ly = a.look[1] + (b.look[1] - a.look[1]) * ease; const lz = a.look[2] + (b.look[2] - a.look[2]) * ease; - // Smooth camera movement - smooth.current.x += (tx - smooth.current.x) * 0.06; - smooth.current.y += (ty - smooth.current.y) * 0.06; - smooth.current.z += (tz - smooth.current.z) * 0.06; - smooth.current.lx += (lx - smooth.current.lx) * 0.06; - smooth.current.ly += (ly - smooth.current.ly) * 0.06; - smooth.current.lz += (lz - smooth.current.lz) * 0.06; + smooth.current.x += (tx - smooth.current.x) * 0.05; + smooth.current.y += (ty - smooth.current.y) * 0.05; + smooth.current.z += (tz - smooth.current.z) * 0.05; + smooth.current.lx += (lx - smooth.current.lx) * 0.05; + smooth.current.ly += (ly - smooth.current.ly) * 0.05; + smooth.current.lz += (lz - smooth.current.lz) * 0.05; camera.position.set(smooth.current.x, smooth.current.y, smooth.current.z); camera.lookAt(smooth.current.lx, smooth.current.ly, smooth.current.lz); - - // Smooth FOV (zoom effect) - const targetFov = a.fov + (b.fov - a.fov) * ease; - smooth.current.fov += (targetFov - smooth.current.fov) * 0.05; - (camera as THREE.PerspectiveCamera).fov = smooth.current.fov; - (camera as THREE.PerspectiveCamera).updateProjectionMatrix(); }); return null; @@ -832,14 +375,14 @@ function TraceCamera() { function PCBScene() { return ( - - - - - + + + + - + + ); } @@ -870,28 +413,37 @@ export default function WebGLBackground() { aria-hidden="true" > - + - - - - + + + + - + - - - + + + ); } + +/* Preload all models */ +useGLTF.preload('/assets/models3d/pcb-bmu-v2.glb'); +useGLTF.preload('/assets/models3d/resistor_0603.glb'); +useGLTF.preload('/assets/models3d/capacitor_0805.glb'); +useGLTF.preload('/assets/models3d/inductor_0805.glb'); +useGLTF.preload('/assets/models3d/led_0603.glb'); +useGLTF.preload('/assets/models3d/soic8.glb'); +useGLTF.preload('/assets/models3d/qfp32.glb');