import {DenseNetwork} from './network.js'; import {MotionProfileClock} from './profile-clock.js'; import {decodeMotionPose} from './controller.js'; /** Experimental profile-conditioned controller. Host engine owns physics. * This checkpoint supports the profiles and synthetic transitions in config. */ export class MotionProfileController { constructor(manifest,weights,config) { if(config?.format!=='superhq-motion-profile-controller-v1'||config.dt!==1/60|| manifest.layers[0]?.inputs!==34||manifest.layers.at(-1)?.outputs!==143|| !Array.isArray(config.rig?.end_sites)||config.rig.end_sites.filter(v=>!v).length!==23|| !config.profiles?.Idle_Loop||!Array.isArray(config.transitions))throw Error('Incompatible profile controller.'); this.network=new DenseNetwork(manifest,weights);this.config=config;this.rig=config.rig;this.reset(); } static async load(baseURL,{fetch:fetcher=globalThis.fetch}={}) { const base=baseURL instanceof URL?baseURL:new URL(baseURL,globalThis.location?.href); const request=async(path,binary=false)=>{const r=await fetcher(new URL(path,base));if(!r.ok)throw Error(`Cannot load ${path}: HTTP ${r.status}`);return binary?r.arrayBuffer():r.json();}; const [manifest,config]=await Promise.all([request('network.json'),request('profile-controller.json')]); if(manifest.weights!=='weights.bin')throw Error('Unexpected weight filename.'); const weights=await request(manifest.weights,true); if(!globalThis.crypto?.subtle)throw Error('Weight verification requires HTTPS or localhost.'); const digest=Array.from(new Uint8Array(await crypto.subtle.digest('SHA-256',weights)),v=>v.toString(16).padStart(2,'0')).join(''); if(digest!==manifest.sha256)throw Error('Motion weight checksum mismatch.'); return new MotionProfileController(manifest,weights,config); } reset({profile='Idle_Loop',rate=1,phase=0,position=[0,0,0]}={}) { const value=this.config.profiles[profile];if(!value)throw Error('Unknown motion profile.'); const clock=new MotionProfileClock(value,{rate});clock.reset(value,{rate,phase,position}); this.clock=clock;this.profile=profile; } setProfile(name,{rate=1}={}) { if(!this.config.profiles[name])throw Error('Unknown motion profile.'); if(this.clock.time-this.clock.start(a===name&&b===this.profile)||(b===name&&a===this.profile)); if(!allowed)throw Error('This transition has not been validated; use reset to select a separate profile.'); this.clock.setProfile(this.config.profiles[name],{rate});this.profile=name; } step() { const state=this.clock.step(),output=this.network.forward(state.input); return {...decodeMotionPose(output,this.rig,state.simulationPosition),...state,profile:this.profile}; } }