import * as THREE from 'three'; import {EffectComposer} from 'three/addons/postprocessing/EffectComposer.js'; import {RenderPass} from 'three/addons/postprocessing/RenderPass.js'; import {UnrealBloomPass} from 'three/addons/postprocessing/UnrealBloomPass.js'; export function createLaunchScene(element){ let renderer; try {renderer=new THREE.WebGLRenderer({alpha:true,antialias:true,powerPreference:'low-power'});}catch{return {setMode(){},setProgress(){},dispose(){}};} renderer.setPixelRatio(Math.min(devicePixelRatio,1.6));renderer.setClearColor(0x090b13,0);renderer.outputColorSpace=THREE.SRGBColorSpace;renderer.toneMapping=THREE.ACESFilmicToneMapping; element.appendChild(renderer.domElement);renderer.domElement.setAttribute('aria-hidden','true'); const scene=new THREE.Scene(),camera=new THREE.PerspectiveCamera(38,1,.1,100);camera.position.set(0,.3,8.4); const composer=new EffectComposer(renderer);composer.addPass(new RenderPass(scene,camera));const bloom=new UnrealBloomPass(new THREE.Vector2(512,512),.85,.75,.72);composer.addPass(bloom); scene.add(new THREE.AmbientLight(0x899bff,2.5)); for(const [color,intensity,pos]of [[0xc8deff,15,[3,5,4]],[0x6357ff,18,[-3,0,2]],[0x57d7ff,10,[1,-3,3]]]){const l=new THREE.PointLight(color,intensity,20);l.position.set(...pos);scene.add(l);} const craft=new THREE.Group();craft.rotation.z=-.48;craft.rotation.x=.12;scene.add(craft); const metal=new THREE.MeshPhysicalMaterial({color:0xbecaf1,metalness:.9,roughness:.23,clearcoat:1}); const blue=new THREE.MeshPhysicalMaterial({color:0x4249d9,metalness:.65,roughness:.25,clearcoat:1}); const dark=new THREE.MeshPhysicalMaterial({color:0x101935,metalness:.8,roughness:.2}); const glow=new THREE.MeshBasicMaterial({color:0x8aacff}); const profile=[new THREE.Vector2(.32,-.9),new THREE.Vector2(.45,-.66),new THREE.Vector2(.46,.45),new THREE.Vector2(.39,.95),new THREE.Vector2(.22,1.32),new THREE.Vector2(0,1.57)]; const hull=new THREE.Mesh(new THREE.LatheGeometry(profile,64),metal);craft.add(hull); const nose=new THREE.Mesh(new THREE.LatheGeometry([new THREE.Vector2(.39,.95),new THREE.Vector2(.22,1.32),new THREE.Vector2(0,1.58)],64),blue);craft.add(nose); const ring=(radius,tube,mat,y)=>{const m=new THREE.Mesh(new THREE.TorusGeometry(radius,tube,12,80),mat);m.rotation.x=Math.PI/2;m.position.y=y;craft.add(m);return m;}; ring(.455,.018,blue,.42);ring(.444,.012,glow,-.42);ring(.345,.07,dark,-.88); const windowFrame=new THREE.Mesh(new THREE.TorusGeometry(.195,.045,16,48),dark);windowFrame.position.set(0,.33,.44);craft.add(windowFrame); const windowGlass=new THREE.Mesh(new THREE.SphereGeometry(.175,32,24),new THREE.MeshPhysicalMaterial({color:0x6fafff,emissive:0x2463da,emissiveIntensity:1.1,metalness:.65,roughness:.12}));windowGlass.scale.z=.25;windowGlass.position.set(0,.33,.465);craft.add(windowGlass); for(let i=0;i<3;i++){ const shape=new THREE.Shape();shape.moveTo(.32,-.35);shape.lineTo(.95,-1.02);shape.quadraticCurveTo(.98,-1.2,.79,-1.08);shape.lineTo(.31,-.8);shape.closePath(); const fin=new THREE.Mesh(new THREE.ExtrudeGeometry(shape,{depth:.075,bevelEnabled:true,bevelSegments:2,steps:1,bevelSize:.025,bevelThickness:.025}),blue);fin.rotation.y=i*Math.PI*2/3;craft.add(fin); } const exhaust=new THREE.Group();exhaust.position.y=-.96;craft.add(exhaust); const flameMat=new THREE.ShaderMaterial({transparent:true,depthWrite:false,blending:THREE.AdditiveBlending,side:THREE.DoubleSide,uniforms:{time:{value:0}},vertexShader:`varying vec2 vUv;void main(){vUv=uv;gl_Position=projectionMatrix*modelViewMatrix*vec4(position,1.);}`,fragmentShader:`varying vec2 vUv;uniform float time;void main(){float f=pow(1.-vUv.y,1.4);float a=f*(.5+.2*sin(vUv.y*35.-time*14.));vec3 c=mix(vec3(.27,.21,1.),vec3(.7,.95,1.),f);gl_FragColor=vec4(c*1.8,a*.65);}`}); const flame=new THREE.Mesh(new THREE.ConeGeometry(.29,1.9,40,1,true),flameMat);flame.rotation.z=Math.PI;flame.position.y=-.9;exhaust.add(flame); const orbital=new THREE.Group();orbital.rotation.x=.38;orbital.rotation.z=-.25;scene.add(orbital); for(let i=0;i<3;i++){ const points=[];for(let j=0;j<=180;j++){const a=j/180*Math.PI*2;points.push(new THREE.Vector3(Math.cos(a)*(2.0+i*.29),Math.sin(a)*(2.0+i*.29),0));} const orbit=new THREE.Line(new THREE.BufferGeometry().setFromPoints(points),new THREE.LineBasicMaterial({color:i===1?0x7376ff:0x4c659a,transparent:true,opacity:i===1?.26:.15}));orbit.rotation.y=.8+i*.36;orbit.rotation.x=.3+i*.38;orbital.add(orbit); } const nodeGeo=new THREE.IcosahedronGeometry(.027,1),nodes=[]; for(let i=0;i<42;i++){ const m=new THREE.Mesh(nodeGeo,new THREE.MeshBasicMaterial({color:i%3===0?0x90ddff:0x686bff,transparent:true,opacity:.65}));const a=i/42*Math.PI*2;const r=2.25+.2*Math.sin(i*2.3);m.position.set(Math.cos(a)*r,Math.sin(a)*r,Math.sin(a*3)*.7);orbital.add(m);nodes.push(m); } const starCount=210,coords=new Float32Array(starCount*3);for(let i=0;i{const w=element.clientWidth,h=element.clientHeight;if(!w||!h)return;renderer.setSize(w,h);composer.setSize(w,h);camera.aspect=w/h;camera.updateProjectionMatrix();};const observer=new ResizeObserver(resize);observer.observe(element);resize(); const intersection=new IntersectionObserver(entries=>{visible=entries[0].isIntersecting;});intersection.observe(element); const pointer={x:0,y:0};element.addEventListener('pointermove',e=>{const b=element.getBoundingClientRect();pointer.x=(e.clientX-b.left)/b.width-.5;pointer.y=(e.clientY-b.top)/b.height-.5;});element.addEventListener('pointerleave',()=>{pointer.x=0;pointer.y=0;}); function draw(t){raf=requestAnimationFrame(draw);if(!visible||document.hidden||mode==='paused'||t-last<(mode==='compile'?125:33))return;const dt=Math.min(.08,(t-last)/1000);last=t;const time=reduced.matches?0:t*.001; const launching=mode==='launch',loading=mode==='loading'||mode==='compile'; const target=launching?5:loading?.8+progress*2.6:.28; thrust+=(target-thrust)*(1-Math.exp(-dt*2.5));if(!reduced.matches)travel+=dt*(.2+thrust*2.5); const age=launching&&!reduced.matches?(t-launchStart)/1000:0,flight=age*age*8; craft.position.set(Math.sin(time*.55)*.13+flight*.45,Math.sin(time*.85)*.2+(loading?progress*.2:0)+flight,0); craft.rotation.y=Math.sin(time*.48)*.65+pointer.x*.2;craft.rotation.x=.1+Math.sin(time*.7)*.09+pointer.y*.12; craft.rotation.z=-.4+Math.sin(time*.42)*.07+(loading?Math.sin(time*24)*.008*thrust:0); orbital.rotation.y=time*(loading?.28:.12);orbital.rotation.z=-.25+Math.sin(time*.16)*.16; flameMat.uniforms.time.value=time;exhaust.scale.y=.7+thrust*.45+Math.sin(time*(loading?22:5))*.08;exhaust.scale.x=exhaust.scale.z=.9+thrust*.07;engineLight.intensity=3+thrust*5; for(let i=0;i{n.material.opacity=mode==='loading'?(i/42{o.geometry?.dispose();if(o.material){for(const m of Array.isArray(o.material)?o.material:[o.material])m.dispose();}});composer.dispose();renderer.dispose();}}; }