React19+Vite7+TS+Tailwind4+three 2
Prompt
【任务】用 React19+Vite7+TS+Tailwind4+three@0.186 写「多金属·多枚奖牌翼伞高空下降模拟 · Three.js v4」,输出完整可运行代码(vite-plugin-singlefile,中文UI)。文件:main.tsx、App.tsx、ui/MiniMap.tsx、engine/{Engine,svgData,medalAssets,suspension,flight,terrain,clouds,canopy,camera,post,audio,rng,worldShared}.ts。内容:翼伞→悬挂板→每枚奖牌用双缎带挂着,可挂 1–6 枚金/银/铜牌,从高空下降飞向体育场,全程能看到地面。 【硬约束】C1 SVG 是真值,d/r/坐标/颜色/opacity/线宽不能改。C2 三种牌几何相同,只换配色/PBR/数字网格;厚/径≈0.11;正反两面一致、都正立(背面组 rotY=π)。C3 N=1 时居中,偏角/深度/相位为0,scale=1。C4 切换材质或数量不重置飞行、相机、种子。C5 资源在初始化时预建,切换只换引用。C6 奖牌之间不穿插、不 z-fight。C7 增删奖牌时保留 id 和相位。C8 title/desc/aria/softGlow 不渲染,不加载位图。C9 全程能看到地面,有空气透视。C10 缎带只受拉,每枚牌由扣环左右两条缎带拉住。C11 固定步长 1/120。C12 结果确定:风用纯函数噪声,奖牌相位用独立 PRNG,同一 T_sim 状态逐位一致。C13 伞绳最小像素宽。C15 60FPS。C16 环境贴图上下半球异色。 【rng.ts】hash32(a,b,c):h=(a|0)^0x9e3779b9;h=imul(h^b,0x85ebca6b);h^=h>>>13;h=imul(h^c,0xc2b2ae35);h^=h>>>16;h=imul(h,0x27d4eb2f);h^=h>>>15;返回 >>>0。mulberry32 用标准实现。medalPhaseRng(base,id)=mulberry32(hash32(base,0x4d45444c,id))。vnoise2 是 lattice=hash32(s,ix,iy)/2^32 + fade t²(3-2t) 的双线性插值,值域[0,1]。fbm2(s,x,y,oct=5):amp 0.5 起每层减半,频率×2.03,第 i 层 seed+i*131,最后归一化。vnoise1(s,t) 用 lattice(s,i,7)*2-1 插值,值域[-1,1]。另有 clamp/smoothstep(a,b,x)=ss/lerp。 【svgData.ts】GOLD_SVG 逐字如下: <?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" width="1180" height="1530" viewBox="0 0 118 153" role="img" aria-label="Gold medal with blue ribbon"> <title>High-fidelity vector reconstruction of medal icon</title> <desc>Pure vector reconstruction from a 118x153 raster reference. No embedded bitmap.</desc> <defs> <linearGradient id="goldFace" x1="0" y1="0" x2="0.75" y2="1"><stop offset="0" stop-color="#FFD24A"/><stop offset="1" stop-color="#FFC83D"/></linearGradient> <linearGradient id="goldRim" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#FFC53B"/><stop offset="1" stop-color="#FFB52E"/></linearGradient> <filter id="softGlow" x="-20%" y="-20%" width="140%" height="140%"><feGaussianBlur stdDeviation="0.28"/></filter> </defs> <path d="M60.85 39.45 L79.75 39.45 L64.05 68.05 L48.05 68.05 Z" fill="#2F7FC9"/> <path d="M30.45 39.45 L50.15 39.45 L63.05 68.05 L48.05 68.05 Z" fill="#42A5F4"/> <path d="M42.55 65.90 L47.55 65.90 L47.55 68.75 L63.75 68.75 L63.75 65.90 L68.80 65.90 L68.80 72.70 C66.25 74.85 62.10 76.10 58.05 76.70 C61.45 77.60 64.15 79.10 66.25 81.10 L62.35 84.05 C60.30 81.85 58.20 80.80 55.70 80.80 C53.25 80.80 51.00 81.85 49.10 84.05 L45.20 81.10 C47.20 79.05 49.85 77.60 53.30 76.70 C49.25 76.05 45.05 74.85 42.55 72.70 Z" fill="#FFC338"/> <ellipse cx="55.45" cy="72.55" rx="1.35" ry="1.85" fill="#FFF2B4" opacity="0.92"/> <circle cx="55.70" cy="98.55" r="23.05" fill="url(#goldRim)"/> <circle cx="55.70" cy="98.55" r="20.65" fill="url(#goldFace)"/> <circle cx="55.70" cy="98.55" r="18.75" fill="none" stroke="#FFA83A" stroke-width="1.55" opacity="0.96"/> <circle cx="55.70" cy="98.55" r="17.55" fill="none" stroke="#FFD75B" stroke-width="0.55" opacity="0.90"/> <path d="M38.20 91.15 C40.55 84.75 45.05 80.55 50.15 78.95" fill="none" stroke="#FFF9D9" stroke-width="1.65" stroke-linecap="round" opacity="0.98"/> <path d="M69.95 107.45 C72.25 104.45 73.45 101.10 73.70 97.20" fill="none" stroke="#FFFBE4" stroke-width="1.85" stroke-linecap="round" opacity="0.98"/> <path d="M54.25 84.95 C53.35 86.00 51.85 86.95 49.85 87.55 L49.85 91.55 L53.15 90.70 L53.15 106.25 L49.80 106.25 L49.80 111.20 L62.15 111.20 L62.15 106.25 L58.75 106.25 L58.75 84.95 Z" fill="#FF9E35"/> <path d="M55.65 84.95 L58.75 84.95 L58.75 106.25 L62.15 106.25 L62.15 111.20 L55.65 111.20 Z" fill="#FFA23A" opacity="0.40"/> </svg> SILVER_SVG/BRONZE_SVG:根元素只有 xmlns、width、height、viewBox(与金牌相同);没有 xml 头、role、aria、title、desc、filter,也没有扣环和 ellipse。渐变 id 为 silverFace/silverRim(铜:bronzeFace/bronzeRim),坐标同金牌,颜色按下表。元素顺序:2 条丝带 → 4 个 circle → 2 条高光弧(几何同金牌,颜色按下表)→ 数字 path(numeralPathD, fill 主色) → 阴影 path。银阴影:d="M44.5 106.0 L54.5 96.5 L54.5 111.0 L44.5 111.0 Z" fill="#A7ACB2" opacity="0.35";铜阴影:d="M53.3 96.3 L59.6 96.3 L59.6 107.5 L53.3 107.5 Z" fill="#B56A33" opacity="0.35"。 MEDAL_PALETTES 字段顺序:metal,rough | faceColor | rimColor | ring(r18.75,r17.55) | hi(左上,右下) | clasp,claspHighlight | numeral | numeralColor(主,侧) gold .90,.28|FFD24A,FFC83D|FFC53B,FFB52E|FFA83A,FFD75B|FFF9D9,FFFBE4|FFC338,FFF2B4|'1'|FF9E35,FFA23A;numeralPathD=金 SVG 数字 1 的 d silver .92,.20|F2F3F5,C9CDD2|DADDE1,AEB2B8|9BA0A6,E6E8EA|FFFFFF,F4F6F8|D4D7DB,F7F9FB|'2'|8A9099,A7ACB2;d="M49.5 89.5 C49.5 85.0 53.0 82.5 56.5 82.5 C60.5 82.5 63.5 85.0 63.5 88.5 C63.5 92.0 61.0 94.5 57.5 97.5 L50.0 104.5 L50.0 106.5 L64.0 106.5 L64.0 111.0 L44.5 111.0 L44.5 106.0 L54.5 96.5 C57.0 94.2 58.5 92.3 58.5 89.8 C58.5 87.8 57.2 86.3 55.0 86.3 C52.8 86.3 51.3 87.8 51.2 90.3 Z" bronze .75,.38|E8955B,C97A3E|D98A4C,B56A33|9C5324,F0B07A|FFE9CF,FFF2E0|D98A4C,FFE3C4|'3'|9C5324,B56A33;d="M49.0 89.0 C49.0 85.0 52.5 82.3 56.5 82.3 C60.5 82.3 63.8 84.8 63.8 88.5 C63.8 91.2 62.0 93.0 59.5 93.8 C62.5 94.5 64.5 96.5 64.5 99.8 C64.5 104.5 60.8 107.5 56.3 107.5 C52.0 107.5 48.5 105.0 48.0 100.8 L52.8 100.0 C53.2 102.3 54.5 103.5 56.3 103.5 C58.3 103.5 59.6 102.2 59.6 100.0 C59.6 97.5 58.0 96.3 55.3 96.3 L53.3 96.3 L53.3 92.5 L55.3 92.5 C57.8 92.5 59.0 91.3 59.0 89.3 C59.0 87.3 57.6 86.2 55.8 86.2 C54.0 86.2 52.8 87.3 52.5 89.6 Z" 数字位置按原坐标放,不做居中修正。常量:CX=55.70,CY=98.55,SVG_R=23.05,R_W=1.5,D=3,k=R_W/SVG_R。 【medalAssets.ts】bakeFace:1024² canvas,先用 rimColor[1] 铺底,setTransform(s,0,0,s,512-CX·s,512-CY·s),s=1024/(2R)。遍历 SVG 子元素:circle 如果是渐变填充,用 objectBoundingBox 线性渐变填充;circle 如果 fill=none,就描边;path 如果 fill=none,就按 stroke/width/linecap 用 Path2D 描边;globalAlpha=opacity。丝带、扣环、数字、阴影不进贴图。贴图用 CanvasTexture,SRGB,aniso≤8,并 initTexture 预上传。用 SVGLoader 按 fill==数字主色找数字 path,与 numeralPathD 比对,不一致就 warn。svgExtrude:Extrude(depth, bevelThickness=b, bevelSize=.8b, bevelSegments=2, curveSegments=20) 后 translate(-CX,-CY),再 scale(k,-k,-k),然后把 minZ 移到 0。数字 depth1.1/bevel.25;扣环取金 SVG 的 #FFC338 path,depth1.6/bevel.3,三种牌共用;#FFF2B4 椭圆转 ShapeGeometry(y 翻转后要反转绕序)。尺寸:ht=.11D/2,recess=.012D,rFace=20.65k,claspGap=.006D,anchorL/R=(∓.30D,(CY-65.9)k,0)。网格:edge=开口圆柱(R_W,高2ht,128段);rimRing=Ring(rFace,R_W);face=Circle(rFace),平面映射 UV;wall=开口圆柱(rFace,高recess);t1=Torus(18.75k,.775k),z 缩放.55;t2=Torus(17.55k,.275k),z 缩放.5;pin 位于 y=(CY-67.3)k;两个锚点处各一个 eyelet。材质都用 Standard+alphaHash:face(map)、rim(DoubleSide)、ring、numFront、numSide、clasp、claspHi(透明度.92)。正面和背面两组,每组:rimRing z=ht;face/t1/t2/数字×3(只显示当前类型)z=ht-recess;wall z=ht-recess/2;clasp z=ht+gap;claspHi 放在 clasp 前表面+.002。所有网格都投射和接收阴影。applyPalette:换贴图和 metal/rough;rim 色=rimColor[1];ring 色=ring[0],粗糙度×.9;numFront 色=主色;numSide 色=侧色,粗糙度+.05;clasp 色=claspColor;claspHi 色=高光色,金属度×.5,粗糙度.2;然后切换数字可见性。数字阴影 path 不挤压。 【suspension.ts(XPBD)】NSEG=5(4 个自由节点),RIB_MASS=.3,MEDAL_MASS=120,L0=2.2,AH=.175D,RIB_DZ=.012D,条带 24 行,iterations=10;预建 6 个 MedalRig。solveLayout(i,N):c=(N-1)/2;step=clamp(70/N,8,14)°;θ=(i-c)step;按 |i-c|、再按 i 排出 rank,z=-rank·.18D;scale=1-.06|i-c|;ell=L0(1+.012|i-c|);N≥4 时 width=clamp(1-.05(N-3),.75,1)。yC=anchorL.y·s,xC=anchorR.x·s;C=rot_θ(0,-ell-yC),CL=C+rot_θ(-xC,yC),CR=C+rot_θ(xC,yC)。在[-6,6]上二分 60 次求 sh,使直线 (sh-AH,0)→CL 与 (sh+AH,0)→CR 的交点 x=C.x(三力汇交)。restL/R=hypot(dx,dy,RIB_DZ)。锚点(Pendulum 局部坐标)=(sh∓AH,0,z±RIB_DZ)。缎带 L(0x42A5F4) 连 A_L→claspLeft,R(0x2F7FC9) 连 A_R→claspRight;材质 rough.55、metal0、DoubleSide;条带每帧重建,宽度×width。刚体:m=120s²,I=(mR²/4,mR²/4,mR²/2)。placeAtRest:x=pend·(sinθ(ell+yC),-cosθ(ell+yC),z);q=pendQ·rotZ(θ+amp·sinφ),perturb 时 amp=.12,ωz=.9amp·cosφ,vx=1.2amp·cosφ;节点在锚点与扣环之间线性插值。step(h,{gEff=(-ax,-G-ay,-az),airVel,rho,pendA,pendB,t,groundY}):锚点在步初/步末矩阵之间插值;力包括重力、二次阻力和缎带分段气动力(后掠、扭转);距离约束只在 len>rest 时修正(只受拉,松弛时下垂)+弯曲约束;扣环与刚体耦合,按 ΣT×r 算力矩;展示用的自转以力矩注入;钳制速度和位移;落地碰撞;出现 NaN 就回滚快照并计数;张力做平滑。setCount:保留 id,相位 φ=(N==1)?0:i·.37+phaseRng()·.2;按 realTime 做 .4s 过渡(新增的从中心淡入展开,移除的收拢淡出,中途再操作就从当前状态继续)。render(alpha) 插值;angleError() 计算静风姿态误差。 【flight.ts】DEFAULT_ENV={H0:3500,windSpeed:5,windDir:250,gust:.45,cloud:.55,hour:15.5};G=9.81;CLEARANCE=6.4;rho(h)=1.225·max(.2,1-2.25577e-5h)^4.2559。阶段:freefall→opening→glide→flare→landed。热气流:mulberry32(hash32(seed,0x5448524d)) 生成 14 个,x/z∈±9000,r=250+300r,w=1+1.6r。风吹向=(-sin a,cos a),a=来向角(x 东、z 南)。target=(20,0)。初始:dist=min(8600,2.3H0),起点=target+(-.78,-.62)dist,heading=atan2(.62,.78),y=H0+地形+6.4,v=(20cos,-4,20sin)。wind:shear=(clamp(y,10,4000)/1000)^.2;gx=vn(s+11,t/7)·.6+vn(s+12,t/2.3)·.25;gz=vn(s+13,t/7.5)·.6+vn(s+14,t/2.1)·.25;wy=vn(s+15,t/1.7)·.5g+Σ热气流 w·e^(-d²/r²)·ss(30,400,y)(1-ss(1800,2700,y))(d²/r²<9 才计算);水平风=dir·ws+g向量·ws·.9·gust。step:t>1.6 进入 opening;opening 时 open+=h/2.6,满后进入 glide;agl<14 进入 flare。自动驾驶:默认朝向目标;agl>140 且 d<min(2.08agl,2600) 时以 Rc=520 盘旋(θ+π/2+clamp((d-Rc)/Rc,±1)·.9);agl>140 的其他情况加 .28sin(t/23) 蛇行。bank 目标:glide 为 clamp(1.1diff,±.45),flare 为 clamp(.4diff,±.1),τ=1.2;brake 目标:flare 时 ss(14,2,agl),τ=.6。dens=√(1.225/ρ),Va=(10.6-5.2b)dens,sink=(3.65-2.4b)dens/cos(bank)^1.5,heading+=G·tan(bank)/max(4,Va)·h·open。自由落体用 k=.0045 二次阻力(相对风);开伞后速度向 (heading·Va+风,-sink+wy) 逼近,τ 在 opening 时为 .55/(.25+open),否则 1.4。离地≤.05 判定 landed(之后速度每步×.9,open 降到 .35)。a=Δv/h,着陆瞬间 a=-6v。摆动:阻尼振子 w0=1.15、ζ=.22;pitch 驱动=-fwdA/G·.5+vn(s+21,t/3)·.04g;roll 驱动=.9bank+latA/G·.3+vn(s+22,t/2.6)·.05g。presim:h=1/120,每 120 步采样一次,直到着陆(上限 3600s)。 【terrain.ts】种子 S=7331。地标:STADIUM(0,0);CITY(3300,1900,半宽1300,半高1000);LAKE(-3600,2600,r650);AIRPORT(-6200,5400,长3000,宽60,角.26);6 个城镇:(-9000,-3000,450)(-4200,-8200,380)(4800,-6800,420)(-12500,4200,500)(1800,7600,350)(-1800,-3600,300)。远景范围 ±20km,近景范围 ±3km。coastX(z)=7000+1800sin(z/6500+.6)+1600(fbm(S+3,z/4000,.5,4)-.5);riverZ(x)=-1400+900sin(x/2600)+400sin(x/900+1.3);河宽 40–110。高度 h=45+230(fbm(S,x/7000+3.1,z/7000-1.7,4)-.42)+60(fbm(S+9,x/1700,z/1700,3)-.5),x<-7000 抬升成山,下限 8,近海岸按 ss(-50,2200,dc)+2 衰减,海面为 -1;河谷用 e^(-rd²/420²) 下切到 3;湖心 6,体育场 28,城市 14,机场 40(平整化)。地表色:海由浅蓝到深蓝,河/湖深蓝青,沙滩、草地/荒地噪声混色、森林、西部岩地、城市灰,加噪声抖动。农田:随机旋转的条状地块,9 种作物色,α.62,近景加垄线,避开水、林、城市和地标。7 条道路(#8d8a84,中线虚线 #e8e2c8)连接体育场、城市、各城镇和机场,并延伸到地图边缘。buildTerrain 返回 {group,farCanvas}:远景 2048² 合成图贴在大地形上,近景用细节图+细网格;城市楼群用 Instanced,体育场有碗形看台、草坪和跑道,另有机场跑道、城镇、树木和反光水面;全部经过 patchWorldMaterial。LOD 按 AGL:>1500 为 L0 烘焙合成图,>600 为 L1,>150 为 L2 植被,其余 L3。 【worldShared.ts】WU uniforms:uCurv,uFogDensity=1.15e-4,uFogHs=2600,uAltOffset,uHazeColor,uSunDir,uSunColor,uSkyAmb,uCloudFog,uTime,uZenith,uHorizon,uHorizonDip。applyAerial:τ=密度·dist·e^(-平均海拔/Hs)+cloudFog·dist/70;haze=雾色+太阳色·mie(dot^6)·.45,有云雾时趋向白;先按 f·.55 去饱和,再混入雾色。patchWorldMaterial:顶点着色器做曲率下沉 y-=|dxz|²·uCurv(支持 instancing),片元着色器调用 applyAerial。天空:地平线到天顶按 ss^.45 混色,太阳光晕 pow8·.35+pow64·.6,地平线以下为雾色,加太阳圆盘;天空球 r1000 跟随相机。createEnvScene:上半球天空、下半球 uGround,供 PMREM 生成环境贴图。 【clouds.ts】卷云平面 140km,y=8600。puff 用实例化 billboard 着色器:曲率下沉、soft particle、受光/背光、前向散射、空气透视,上限 1400 个。generate(hash32(seed,0x434c4f44)):中层 10+40c 团(y2000–2800,r300–800);低层 6+20c 团(y450–800);航迹上保证一次中层穿云(y2450)和一次低层碎云掠过(y640)。insideFactor=ss(1.1,.35,椭球 q)。 【canopy.ts】15 个 cell,RA=8,YC=.95,AMAX=.62,弦长=3.3(1-.28(a/AMAX)²),NACA 剖面·.85(上表面×1.25、下表面×.55),cell 中部鼓起。颜色顺序:深蓝#1d3f7a×2,米白#f4efe2,红#d62a24×2,米白,黄#e8b52c,红,黄,米白,红×2,米白,深蓝×2。背光时透光(emissive=diffuse·uTrans)。伞绳每侧 6 肋×2 排,共 24 根,汇到 (±.42,1.55,-.1),带微弧垂和后掠,r=.006,管线着色器用 max(r,dist·uPix) 保证最小宽度。两根 riser(r.028) 连到转环 y=.42;转环下是悬挂板,按奖牌跨度缩放。setOpen 用 1-(1-o)³ 缩放伞衣。 【camera.ts】平滑用 realTime,k=1-e^(-2.6dt)。预设:reference:az=sunAz+π+.62,el.35,dist40,fov38,target y-1.8;wide:+.3,el.55,dist150,fov55;closeup:对准前排奖牌,faceAz+.25,el.12,dist7.5,fov30;silhouette:sunAz+π,el-.5,dist34,fov50;ground:着陆点+(150,95)、离地 1.7m,fov=2atan(16/d),限制在 .8–60;free:自由轨道。交互:左键旋转,右键/Shift 平移,滚轮缩放(.15–6)。 【post.ts】HalfFloat RT + 4×MSAA → Bloom(.32,.55,.95) → Output(ACES) → 自定义 Pass:湿镜头水珠、星芒(倍速≥8 时减弱)、太阳眩光+4 个鬼影、出云闪白、晕影、胶片颗粒。【audio.ts】布朗噪声 → 带通 500Hz;音量=min(.5,.05+.018 空速),云中×1.3;倍速>4 时静音(可选随倍速变调,pitch=min(4,√倍速))。 【Engine.ts】渲染器:无 AA,dpr≤1.5,ACES,PCFSoft;相机 fov36,near.5,far170000;seed=20240811;太阳 3.2,阴影 2048、范围 ±12,跟随主体。init 显示分步进度。太阳位置:dayT=(h-6)/13,el=max(.07,.96sin(π·dayT)),az=π/2-π(h-6)/12;高度角低时色暖,强度 .6+2.8ss。respawn:FlightModel+presim,生成云,奖牌回到平衡态。Root 姿态=Euler(pitch,atan2(-cos hd,-sin hd),-roll,'YXZ'),Pendulum 再绕 Y 转 t·.45。主循环:dt≤.05,acc+=dt·倍速,子步上限 8(倍速≥8 时 24),超限就丢弃积压并提示“追帧受限”1.2s;每 120 步与 presim 逐位比对一次(detOk),每 240 步记录一次航迹。渲染:插值;浮动原点;uCurv=6e-7·ss(600,3000,H);dip=2√(H·curv)+√(2H/6.371e6);地面半球色随高度雾化,每 .5s 刷新 PMREM;穿云后触发闪白和镜头湿润;调试模式画约束链,张力着色 HSL(.66-.66t)。API:setMaterial/setCount/setTimeScale(.1–20)/setPaused/stepOnce/setPreset/setDebug/respawn/subscribe(HUD 每 .1s 推送一次)。 【App.tsx】暗色玻璃面板,带加载进度屏。左上 HUD:`🥇 ×N`、阶段、AGL、垂直速度、地速、滑翔比、空速、左右张力色条、倍速(暂停时显示 ⏸)、T_sim、真实耗时、预计剩余、⚠追帧受限、☁穿云中、确定性 ✓/✗、LOD、FPS/Draw;调试模式另显示姿态误差和 NaN 次数。右上环境面板:H0 1500–4000,风速 0–12,风向 0–359,阵风 0–1,云量 0–1,时刻 7–18.5,点“重生”后生效。右下小地图:farCanvas 底图,白色虚线为预测航迹,黄色 #ffcf3a 为实际航迹,红圈为体育场,绿✕为着陆预测,箭头为当前位置,带比例尺和 N↑。底栏:材质 🥇金/🥈银/🥉铜,数量 −/+(1–6),倍速 0.25/.5/1/2/4/8/16 + 滑杆 0.1–20(靠近预设时吸附),暂停,单步,机位 6 个预设,相机重置,风声开关,调试开关。快捷键:空格暂停,. 单步,[ ] 调档。 【画面】主体在上中部、占画高 55–70%,下方 1/3 为地面;侧逆光;天空深蓝渐变到暖白;地面低饱和带雾;伞绳根根可数;奖牌扇形叠挂,前排最大。参考图:https://cloudflarecnimg.scdn.io/i/6acb454595785_1791706437.jpeg
A system prompt was added to support web rendering
Response not available
Response not available