| let scene, camera, renderer, skeletonHelper; |
| let joints = []; |
| let lines = []; |
| let currentMotion = null; |
| let frameIndex = 0; |
|
|
| function init() { |
| scene = new THREE.Scene(); |
| camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
| camera.position.set(2, 2, 5); |
| camera.lookAt(0, 1, 0); |
|
|
| renderer = new THREE.WebGLRenderer({ antialias: true }); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| document.body.appendChild(renderer.domElement); |
|
|
| const grid = new THREE.GridHelper(10, 10, 0x555555, 0x333333); |
| scene.add(grid); |
|
|
| const light = new THREE.PointLight(0xffffff, 1); |
| light.position.set(10, 10, 10); |
| scene.add(light); |
| scene.add(new THREE.AmbientLight(0x444444)); |
|
|
| animate(); |
| } |
|
|
| const PRESETS = { |
| humanoid: [ |
| { name: "root", pos: [0, 1, 0], parent: -1 }, |
| { name: "spine", pos: [0, 1.2, 0], parent: 0 }, |
| { name: "head", pos: [0, 1.5, 0], parent: 1 }, |
| { name: "l_shoulder", pos: [-0.2, 1.3, 0], parent: 1 }, |
| { name: "r_shoulder", pos: [0.2, 1.3, 0], parent: 1 }, |
| { name: "l_elbow", pos: [-0.4, 1.3, 0], parent: 3 }, |
| { name: "r_elbow", pos: [0.4, 1.3, 0], parent: 4 }, |
| { name: "l_leg", pos: [-0.1, 0.9, 0], parent: 0 }, |
| { name: "r_leg", pos: [0.1, 0.9, 0], parent: 0 }, |
| { name: "l_knee", pos: [-0.1, 0.5, 0], parent: 7 }, |
| { name: "r_knee", pos: [0.1, 0.5, 0], parent: 8 }, |
| { name: "l_foot", pos: [-0.1, 0, 0], parent: 9 }, |
| { name: "r_foot", pos: [0.1, 0, 0], parent: 10 }, |
| ], |
| centipede: [ |
| { name: "seg1", pos: [0, 0.2, 0], parent: -1 }, |
| { name: "seg2", pos: [0.5, 0.2, 0], parent: 0 }, |
| { name: "seg3", pos: [1.0, 0.2, 0], parent: 1 }, |
| { name: "seg4", pos: [1.5, 0.2, 0], parent: 2 }, |
| { name: "l1", pos: [0, 0, 0.3], parent: 0 }, |
| { name: "r1", pos: [0, 0, -0.3], parent: 0 }, |
| { name: "l2", pos: [0.5, 0, 0.3], parent: 1 }, |
| { name: "r2", pos: [0.5, 0, -0.3], parent: 1 }, |
| { name: "l3", pos: [1.0, 0, 0.3], parent: 2 }, |
| { name: "r3", pos: [1.0, 0, -0.3], parent: 2 }, |
| ] |
| }; |
|
|
| function buildSkeleton(type) { |
| joints.forEach(j => scene.remove(j)); |
| lines.forEach(l => scene.remove(l)); |
| joints = []; |
| lines = []; |
|
|
| const data = PRESETS[type] || PRESETS.humanoid; |
| data.forEach(d => { |
| const geo = new THREE.SphereGeometry(0.05); |
| const mat = new THREE.MeshBasicMaterial({ color: 0x00ffcc }); |
| const mesh = new THREE.Mesh(geo, mat); |
| mesh.position.set(...d.pos); |
| scene.add(mesh); |
| joints.push(mesh); |
| }); |
|
|
| data.forEach((d, i) => { |
| if (d.parent !== -1) { |
| const geo = new THREE.BufferGeometry().setFromPoints([ |
| new THREE.Vector3(...d.pos), |
| new THREE.Vector3(...data[d.parent].pos) |
| ]); |
| const mat = new THREE.LineBasicMaterial({ color: 0x888888 }); |
| const line = new THREE.Line(geo, mat); |
| scene.add(line); |
| lines.push({ line, start: i, end: d.parent }); |
| } |
| }); |
| } |
|
|
| async function runInference() { |
| const type = document.getElementById("preset").value; |
| const steps = document.getElementById("steps").value; |
| const status = document.getElementById("status"); |
| |
| status.innerText = "Computing Enrichment & Transformer Layers..."; |
| status.style.color = "#ff0"; |
|
|
| const response = await fetch("/infer", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ |
| skeleton: { joints: PRESETS[type] || PRESETS.humanoid }, |
| steps: parseInt(steps) |
| }) |
| }); |
|
|
| const data = await response.json(); |
| currentMotion = data.motion; |
| frameIndex = 0; |
| status.innerText = "Inference Complete. Playing Motion."; |
| status.style.color = "#0f0"; |
| } |
|
|
| function animate() { |
| requestAnimationFrame(animate); |
| |
| if (currentMotion) { |
| const frame = currentMotion[frameIndex]; |
| frame.forEach((pos, i) => { |
| if (joints[i]) joints[i].position.set(pos[0], pos[1], pos[2]); |
| }); |
|
|
| lines.forEach(l => { |
| const p1 = joints[l.start].position; |
| const p2 = joints[l.end].position; |
| l.line.geometry.setFromPoints([p1, p2]); |
| }); |
|
|
| frameIndex = (frameIndex + 1) % currentMotion.length; |
| } |
|
|
| renderer.render(scene, camera); |
| } |
|
|
| window.addEventListener('resize', () => { |
| camera.aspect = window.innerWidth / window.innerHeight; |
| camera.updateProjectionMatrix(); |
| renderer.setSize(window.innerWidth, window.innerHeight); |
| }); |
|
|
| document.getElementById("run").onclick = runInference; |
| document.getElementById("preset").onchange = (e) => buildSkeleton(e.target.value); |
|
|
| init(); |
| buildSkeleton("humanoid"); |
|
|