"use client"; import { useEffect, useRef } from "react"; type Tr = (zh: string, en: string) => string; export function LiveStudioScene3D({ tr }: { tr: Tr }) { const mountRef = useRef(null); useEffect(() => { const el = mountRef.current; if (!el) return; let disposed = false; let animationId = 0; let teardown: (() => void) | undefined; void import("three").then((THREE) => { if (disposed || !mountRef.current) return; const mount = mountRef.current; const width = Math.max(mount.clientWidth, 280); const height = Math.max(mount.clientHeight, 200); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(48, width / height, 0.1, 80); camera.position.set(0, 1.1, 7.2); const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setPixelRatio(Math.min(typeof window !== "undefined" ? window.devicePixelRatio : 1, 2)); renderer.setSize(width, height); renderer.setClearColor(0x000000, 0); mount.appendChild(renderer.domElement); const hemi = new THREE.HemisphereLight(0xffc4ec, 0x4466ff, 0.85); scene.add(hemi); const dir = new THREE.DirectionalLight(0xffffff, 0.9); dir.position.set(4, 8, 6); scene.add(dir); const back = new THREE.PointLight(0x7cf5ff, 0.6, 20); back.position.set(-3, 2, -2); scene.add(back); const group = new THREE.Group(); const studio = new THREE.Mesh( new THREE.BoxGeometry(3.2, 0.35, 2.2), new THREE.MeshStandardMaterial({ color: 0x2a2540, metalness: 0.15, roughness: 0.55, }), ); studio.position.y = -0.85; group.add(studio); const ring = new THREE.Mesh( new THREE.TorusGeometry(0.72, 0.09, 12, 48), new THREE.MeshStandardMaterial({ color: 0xff3355, emissive: 0x440011, metalness: 0.35, roughness: 0.25, }), ); ring.rotation.x = Math.PI / 2; ring.position.set(-0.85, 0.15, 0.2); group.add(ring); const play = new THREE.Mesh( new THREE.ConeGeometry(0.35, 0.5, 3), new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: 0x222222, metalness: 0.2, roughness: 0.3, }), ); play.rotation.z = -Math.PI / 2; play.rotation.y = Math.PI; play.position.set(-0.55, 0.18, 0.2); group.add(play); const tk = new THREE.Mesh( new THREE.CylinderGeometry(0.22, 0.28, 0.95, 20), new THREE.MeshStandardMaterial({ color: 0x25f4ee, emissive: 0x053038, metalness: 0.25, roughness: 0.35, }), ); tk.position.set(1.05, 0.35, -0.15); tk.rotation.z = 0.12; group.add(tk); const tkCap = new THREE.Mesh( new THREE.SphereGeometry(0.26, 16, 16), new THREE.MeshStandardMaterial({ color: 0xfe2c55, emissive: 0x220510, metalness: 0.2, roughness: 0.4, }), ); tkCap.position.set(1.05, 0.92, -0.15); group.add(tkCap); const starGeo = new THREE.BufferGeometry(); const starCount = 120; const positions = new Float32Array(starCount * 3); for (let i = 0; i < starCount; i++) { positions[i * 3] = (Math.random() - 0.5) * 14; positions[i * 3 + 1] = (Math.random() - 0.3) * 8; positions[i * 3 + 2] = (Math.random() - 0.5) * 8 - 2; } starGeo.setAttribute("position", new THREE.BufferAttribute(positions, 3)); const stars = new THREE.Points( starGeo, new THREE.PointsMaterial({ color: 0xffffff, size: 0.06, transparent: true, opacity: 0.7 }), ); scene.add(stars); scene.add(group); const t0 = performance.now(); const tick = () => { if (disposed) return; animationId = requestAnimationFrame(tick); const t = (performance.now() - t0) / 1000; group.rotation.y = Math.sin(t * 0.35) * 0.25 + t * 0.12; group.position.y = Math.sin(t * 0.9) * 0.06; ring.rotation.z = t * 0.4; stars.rotation.y = t * 0.02; renderer.render(scene, camera); }; tick(); const ro = new ResizeObserver(() => { if (!mountRef.current || disposed) return; const w = Math.max(mountRef.current.clientWidth, 280); const h = Math.max(mountRef.current.clientHeight, 200); camera.aspect = w / h; camera.updateProjectionMatrix(); renderer.setSize(w, h); }); ro.observe(mount); teardown = () => { cancelAnimationFrame(animationId); ro.disconnect(); renderer.dispose(); starGeo.dispose(); if (renderer.domElement.parentNode === mount) { mount.removeChild(renderer.domElement); } }; }); return () => { disposed = true; cancelAnimationFrame(animationId); teardown?.(); }; }, []); return (

{tr("直播工作室 · 3D", "Live studio · 3D")}

); }