import * as THREE from 'three' ; import { GLTFNode , RawVector3 , RawVector4 , VRMPose , VRMSchema } from '../types' ; import { quatInvertCompat } from '../utils/quatInvertCompat' ; import { VRMHumanBone } from './VRMHumanBone' ; import { VRMHumanBoneArray } from './VRMHumanBoneArray' ; import { VRMHumanBones } from './VRMHumanBones' ; import { VRMHumanDescription } from './VRMHumanDescription' ; const _v3A = new THREE . Vector3 ( ) ; const _quatA = new THREE . Quaternion ( ) ; /**
 * A class represents humanoid of a VRM.
 */ export class VRMHumanoid { /**
     * A [[VRMHumanBones]] that contains all the human bones of the VRM.
     * You might want to get these bones using [[VRMHumanoid.getBone]].
     */ public readonly humanBones : VRMHumanBones ; /**
     * A [[VRMHumanDescription]] that represents properties of the humanoid.
     */ public readonly humanDescription : VRMHumanDescription ; /**
     * A [[VRMPose]] that is its default state.
     * Note that it's not compatible with `setPose` and `getPose`, since it contains non-relative values of each local transforms.
     */ public readonly restPose : VRMPose = { } ; param boneArray param humanDescription public constructor ( boneArray , humanDescription ) { this . humanBones = this . _createHumanBones ( boneArray ) ; this . humanDescription = humanDescription ; this . restPose = this . getPose ( ) ; } /**
     * Return the current pose of this humanoid as a [[VRMPose]].
     *
     * Each transform is a local transform relative from rest pose (T-pose).
     */ public getPose ( ) { const pose = { } ; Object . keys ( this . humanBones ) . forEach ( ( vrmBoneName ) => { const node = this . getBoneNode ( vrmBoneName as VRMSchema . HumanoidBoneName ) ! ; if ( ! node ) { return ; } if ( pose [ vrmBoneName ] ) { return ; } _v3A . set ( 0 , 0 , 0 ) ; _quatA . identity ( ) ; const restState = this . restPose [ vrmBoneName ] ; if ( restState ?. position ) { _v3A . fromArray ( restState . position ) . negate ( ) ; } if ( restState ?. rotation ) { quatInvertCompat ( _quatA . fromArray ( restState . rotation ) ) ; } _v3A . add ( node . position ) ; _quatA . premultiply ( node . quaternion ) ; pose [ vrmBoneName ] = { position : _v3A . toArray ( ) as RawVector3 , rotation : _quatA . toArray ( ) as RawVector4 , } ; } , { } as VRMPose ) ; return pose ; } /**
     * Let the humanoid do a specified pose.
     *
     * Each transform have to be a local transform relative from rest pose (T-pose).
     * You can pass what you got from  getPose .
     *
     *  param poseObject public setPose ( poseObject ) { Object . keys ( poseObject ) . forEach ( ( boneName ) => { const state = poseObject [ boneName ] ! ; const node = this . getBoneNode ( boneName as VRMSchema . HumanoidBoneName ) ; if ( ! node ) { return ; } const restState = this . restPose [ boneName ] ; if ( ! restState ) { return ; } if ( state . position ) { node . position . fromArray ( state . position ) ; if ( restState . position ) { node . position . add ( _v3A . fromArray ( restState . position ) ) ; } } if ( state . rotation ) { node . quaternion . fromArray ( state . rotation ) ; if ( restState . rotation ) { node . quaternion . multiply ( _quatA . fromArray ( restState . rotation ) ) ; } } } ) ; } /**
     * Reset the humanoid to its rest pose.
     */ public resetPose ( ) { Object . entries ( this . restPose ) . forEach ( ( [ boneName , rest ] ) => { const node = this . getBoneNode ( boneName as VRMSchema . HumanoidBoneName ) ; if ( ! node ) { return ; } if ( rest ?. position ) { node . position . fromArray ( rest . position ) ; } if ( rest ?. rotation ) { node . quaternion . fromArray ( rest . rotation ) ; } } ) ; } param name public getBone ( name ) { return this . humanBones [ name ] [ 0 ] ?? undefined ; } param name public getBones ( name ) { return this . humanBones [ name ] ?? [ ] ; } param name public getBoneNode ( name ) { return this . humanBones [ name ] [ 0 ] ?. node ?? null ; } param name public getBoneNodes ( name ) { return this . humanBones [ name ] ?. map ( ( bone ) => bone . node ) ?? [ ] ; } /**
     * Prepare a [[VRMHumanBones]] from a [[VRMHumanBoneArray]].
     */ private _createHumanBones ( boneArray ) { const bones = Object . values ( VRMSchema . HumanoidBoneName ) . reduce ( ( accum , name ) => { accum [ name ] = [ ] ; return accum ; } , { } as Partial < VRMHumanBones > ) as VRMHumanBones ; boneArray . forEach ( ( bone ) => { bones [ bone . name ] . push ( bone . bone ) ; } ) ; return bones ; } }