Spaces:
Running
Running
| <html><head><meta charset="utf-8"><title>anim test</title> | |
| <style>html,body{margin:0;height:100%;background:#202028;overflow:hidden} | |
| #hud{position:fixed;top:0;left:0;color:#0f0;font:12px monospace;z-index:9;padding:6px;background:#000a;white-space:pre}</style> | |
| <script type="importmap">{"imports":{ | |
| "three":"https://unpkg.com/three@0.160.0/build/three.module.js", | |
| "three/addons/":"https://unpkg.com/three@0.160.0/examples/jsm/", | |
| "fflate":"https://unpkg.com/fflate@0.8.2/esm/browser.js" | |
| }}</script></head> | |
| <body><div id="hud">loading…</div> | |
| <script type="module"> | |
| import * as THREE from 'three'; | |
| import {FBXLoader} from 'three/addons/loaders/FBXLoader.js'; | |
| const hud=document.getElementById('hud');const L=(m)=>hud.textContent=m; | |
| const renderer=new THREE.WebGLRenderer({antialias:true}); | |
| renderer.setPixelRatio(Math.min(devicePixelRatio,1.5));renderer.setSize(innerWidth,innerHeight); | |
| renderer.shadowMap.enabled=true;document.body.appendChild(renderer.domElement); | |
| const scene=new THREE.Scene();scene.background=new THREE.Color(0x202028); | |
| scene.add(new THREE.HemisphereLight(0xffffff,0x445,1.2)); | |
| const dl=new THREE.DirectionalLight(0xffffff,2.2);dl.position.set(3,8,6);dl.castShadow=true;scene.add(dl); | |
| const ground=new THREE.Mesh(new THREE.PlaneGeometry(40,40),new THREE.MeshStandardMaterial({color:0x33343e})); | |
| ground.rotation.x=-Math.PI/2;ground.receiveShadow=true;scene.add(ground); | |
| const cam=new THREE.PerspectiveCamera(45,innerWidth/innerHeight,0.05,100); | |
| cam.position.set(0,1.1,3.6);cam.lookAt(0,1.0,0); | |
| scene.overrideMaterial=new THREE.MeshStandardMaterial({color:0xbfc4cc,roughness:.7}); | |
| const fbx=new FBXLoader(); | |
| const load=p=>new Promise((res,rej)=>fbx.load(p,res,undefined,rej)); | |
| const WHICH=location.hash==='#boss'?'boss':'player'; | |
| const BASE=WHICH==='boss'?'assets/chars/malenia.fbx':'assets/chars/player.fbx'; | |
| const NAMES=WHICH==='boss' | |
| ?['idle','walk','run','attack1','attack2','kick','block','powerup','hit','death'] | |
| :['idle','walk','walkback','run','attack1','attack2','combo','kick','block','hit','roll']; | |
| let mixer,clips={},cur,action; | |
| window.__clip=(n)=>{if(!clips[n])return 'no '+n;const na=mixer.clipAction(clips[n]); | |
| if(action&&action!==na){action.fadeOut(0.2);}na.reset().fadeIn(0.2).play();action=na;cur=n;return {n,dur:+clips[n].duration.toFixed(2)};}; | |
| window.__list=()=>Object.keys(clips).map(n=>({n,dur:+clips[n].duration.toFixed(2)})); | |
| (async()=>{ | |
| L('loading base '+WHICH+'…'); | |
| const base=await load(BASE); | |
| const box=new THREE.Box3().setFromObject(base);const s=new THREE.Vector3();box.getSize(s); | |
| const sc=1.8/s.y;base.scale.setScalar(sc);base.position.y=-box.min.y*sc; | |
| base.traverse(o=>{if(o.isMesh){o.castShadow=true;o.frustumCulled=false;}}); | |
| scene.add(base); | |
| mixer=new THREE.AnimationMixer(base); | |
| const boneNames=new Set();base.traverse(o=>{if(o.isBone)boneNames.add(o.name);}); | |
| for(const n of NAMES){ | |
| try{const a=await load('assets/anim/'+WHICH+'/'+n+'.fbx'); | |
| if(a.animations&&a.animations[0]){clips[n]=a.animations[0];clips[n].name=n;} | |
| else L('no clip in '+n); | |
| }catch(e){console.warn(n,e);} | |
| } | |
| // report track/bone match on first clip | |
| const first=clips[NAMES[0]]; | |
| let matched=0,total=0; | |
| if(first){for(const tr of first.tracks){total++;const bn=tr.name.split('.')[0];if(boneNames.has(bn))matched++;}} | |
| L(`base bones=${boneNames.size} clips=${Object.keys(clips).length} tracksMatched=${matched}/${total}\n__clip(n) __list()`); | |
| window.__loaded={bones:boneNames.size,clips:Object.keys(clips),matched,total}; | |
| if(clips.idle)window.__clip('idle'); | |
| let last=performance.now(); | |
| (function loop(now){requestAnimationFrame(loop);const dt=Math.min((now-last)/1000,0.05);last=now; | |
| if(mixer)mixer.update(dt);renderer.render(scene,cam);})(performance.now()); | |
| })().catch(e=>L('ERR '+e.message+'\n'+(e.stack||''))); | |
| </script></body></html> | |