Initial commit: RHINE LAB · ANALYSIS OS:三维界面与动效实验(本仓库不含个人归档索引数据)

This commit is contained in:
WpyQwq
2026-09-19 13:04:56 +08:00
commit 3f422218a5
1098 changed files with 39410 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>内部模型 · 第一版恢复核对</title>
<style>
*{box-sizing:border-box}body{margin:0;background:#eae5e1;color:#292c28;font:14px Arial,"Microsoft YaHei",sans-serif}main{max-width:1700px;margin:auto;padding:28px}h1{font-size:23px;margin:0 0 12px}p{color:#676962;line-height:1.7}nav{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin:24px 0}select,button,a{padding:9px 12px;border:1px solid #b7b9b0;background:transparent;color:inherit;font:inherit}a{text-decoration:none}label{display:flex;gap:10px;align-items:center}.views{display:grid;grid-template-columns:1fr 1fr;gap:20px}.view{height:440px;position:relative;background:#e1e0d9}canvas{width:100%!important;height:100%!important}h2{font-size:15px;font-weight:500}pre{font:12px monospace;white-space:pre-wrap;color:#666}button[aria-pressed=true]{background:#353b30;color:#f1f0e8}@media(max-width:800px){.views{grid-template-columns:1fr}.view{height:330px}}</style>
<main><h1>内部模型 · 第一版恢复核对</h1><p>右侧已恢复左侧第一版的完整内构与材质,装配模式使用修订后的外壳。两边使用相同灯光和视角。</p>
<nav><label>显示<select id="display"><option value="interior">仅内部结构</option><option value="clear">装配 · 清晰盖板</option><option value="frosted">装配 · 磨砂盖板</option></select></label><button data-angle="oblique" aria-pressed="true">斜向观察</button><button data-angle="front">正面</button><button data-angle="edge">低角度</button><label>缩放<select id="zoom"><option value="1">完整内构</option><option value="1.6">连接处 1.6×</option><option value="2.2">小环内圈 2.2×</option></select></label><a href="/?scene=detail" target="_blank">打开详情体验 ↗</a></nav>
<section class="views"><article><h2>第一版 · 原始内构</h2><div class="view" id="before"></div></article><article><h2>已恢复 · 第一版内构</h2><div class="view" id="after"></div></article></section><pre id="status">正在加载…</pre></main>
<script type="module">
import * as THREE from 'three';
import {GLTFLoader} from 'three/addons/loaders/GLTFLoader.js';
import {ArchiveScene} from '/src/scene.ts';
const $=q=>document.querySelector(q),scenes=[new ArchiveScene($('#before')),new ArchiveScene($('#after'))],groups=[];
let angle='oblique',ready=false;const grids=[];
scenes.forEach(s=>s.composer.render=()=>{});
async function asset(s,path){
const raw=(await new GLTFLoader().loadAsync(path)).scene;raw.updateMatrixWorld(true);const group=new THREE.Group();
raw.traverse(o=>{if(!o.isMesh)return;const m=new THREE.Mesh(o.geometry.clone().applyMatrix4(o.matrixWorld),o.material);m.userData={assemblyPart:o.userData.assemblyPart,surface:o.material.name.replace(/\.\d+$/,'')};group.add(m)});
s.appearance.prepare(group);s.appearance.apply(group,1);s.appearance.setClarity(group,1);group.position.y=-1.85;s.scene.add(group);return group;
}
function draw(){
if(!ready)return;const mode=$('#display').value,zoom=Number($('#zoom').value);
scenes.forEach((s,i)=>{
grids[i].visible=mode==='interior';const g=groups[i];for(const mesh of g.children)mesh.visible=mode!=='interior'||['optical-core','optical-lenses'].includes(mesh.userData.assemblyPart);
s.appearance.setClarity(g,mode==='frosted'?0:1);
const points={front:[.2,.15,7.6],oblique:[2.6,4.5,6.6],edge:[3.8,7.5,3.0]};
const target=new THREE.Vector3(zoom>2?1.125:zoom>1?.43:.12,zoom>2?.605:zoom>1?-.30:.1,0);
const dir=new THREE.Vector3(...points[angle]);s.camera.position.copy(target).add(dir.multiplyScalar((mode==='interior'?.66:1.03)/zoom));s.camera.near=.1;s.camera.far=120;s.camera.fov=34;
s.camera.aspect=s.container.clientWidth/s.container.clientHeight;s.camera.lookAt(target);s.camera.updateProjectionMatrix();s.camera.updateMatrixWorld(true);
s.renderer.setPixelRatio(Math.min(devicePixelRatio,2));s.renderer.setSize(s.container.clientWidth,s.container.clientHeight,false);
s.renderer.render(s.scene,s.camera);
});
$('#status').textContent='已载入 · 两侧内构的几何、法线及原始材质一致。右侧装配模式含已修好的外壳;网格仅用于观察层次。';
}
try{
await Promise.all(scenes.map((s,i)=>s.load(i?'/assets/archive-cassette.glb':'/reference/internal-study/assembly-before.glb')));
for(const s of scenes){s.scene.fog=null;for(const child of s.scene.children)if(child.isMesh||child.isGroup)child.visible=false;}
groups.push(...await Promise.all(scenes.map((s,i)=>asset(s,i?'/assets/archive-assembly.glb':'/reference/internal-study/assembly-before.glb'))));
for(const s of scenes){
const grid=new THREE.GridHelper(8,32,0xaaa99d,0xbab9ae);grid.rotation.x=Math.PI/2;grid.position.z=-.065;s.scene.add(grid);grids.push(grid);
}
ready=true;draw();
}catch(e){$('#status').textContent=String(e);console.error(e)}
document.querySelectorAll('[data-angle]').forEach(b=>b.onclick=()=>{angle=b.dataset.angle;document.querySelectorAll('[data-angle]').forEach(x=>x.setAttribute('aria-pressed',String(x===b)));draw()});
let queued=false;const queueDraw=()=>{if(queued)return;queued=true;requestAnimationFrame(()=>{queued=false;draw()})};
$('#display').oninput=queueDraw;$('#display').onchange=queueDraw;$('#zoom').onchange=queueDraw;addEventListener('resize',queueDraw);
</script></html>