import type Armature from '../armature/Armature' ; import type Bone from '../armature/Bone' ; import type Clip from './Clip' ; import { vec3 , quat } from 'gl-matrix' ; import QuatUtil from '../maths/QuatUtil' ; import Vec3Util from '../maths/Vec3Util' ; import Animator from './Animator' ; import Pose from '../armature/Pose' ; import BoneMap , { BoneInfo , BoneChain } from '../armature/BoneMap' ; class Source { arm : Armature ; pose : Pose ; posHip = vec3 . create ( ) ; constructor ( arm ) { this . arm = arm ; this . pose = arm . newPose ( ) ; } } class BoneLink { fromIndex : number ; fromName : string ; toIndex : number ; toName : string ; quatFromParent = quat . create ( ) ; quatDotCheck = quat . create ( ) ; wquatFromTo = quat . create ( ) ; toWorldLocal = quat . create ( ) ; constructor ( fIdx , fName , tIdx , tName ) { this . fromIndex = fIdx ; this . fromName = fName ; this . toIndex = tIdx ; this . toName = tName ; } bind ( fromTPose , toTPose ) { const fBone = fromTPose . bones [ this . fromIndex ] ; const tBone = toTPose . bones [ this . toIndex ] ; quat . copy ( this . quatFromParent , ( fBone . pidx != - 1 ) ? fromTPose . bones [ fBone . pidx ] . world . rot : fromTPose . offset . rot ) ; if ( tBone . pidx != - 1 ) quat . invert ( this . toWorldLocal , toTPose . bones [ tBone . pidx ] . world . rot ) ; else quat . invert ( this . toWorldLocal , toTPose . offset . rot ) ; quat . invert ( this . wquatFromTo , fBone . world . rot ) ; quat . mul ( this . wquatFromTo , this . wquatFromTo , tBone . world . rot ) ; quat . copy ( this . quatDotCheck , fBone . world . rot ) ; return this ; } } class Retarget { hipScale = 1 ; anim = new Animator ( ) ; map : Map < string , BoneLink > = new Map ( ) ; from ! : Source ; to ! : Source ; setClip ( c ) { this . anim . setClip ( c ) ; return this ; } setClipArmature ( arm ) { this . from = new Source ( arm ) ; return this ; } setClipPoseOffset ( rot ? , pos ? , scl ? ) { const p = this . from . pose ; if ( rot ) quat . copy ( p . offset . rot , rot ) ; if ( pos ) vec3 . copy ( p . offset . pos , pos ) ; if ( scl ) vec3 . copy ( p . offset . scl , scl ) ; return this ; } setTargetArmature ( arm ) { this . to = new Source ( arm ) ; return this ; } getClipPose ( doUpdate = false , incOffset = false ) { if ( doUpdate ) this . from . pose . updateWorld ( incOffset ) ; return this . from . pose ; } getTargetPose ( doUpdate = false , incOffset = false ) { if ( doUpdate ) this . to . pose . updateWorld ( incOffset ) ; return this . to . pose ; } bind ( ) { const mapFrom = new BoneMap ( this . from . arm ) ; const mapTo = new BoneMap ( this . to . arm ) ; this . from . pose . updateWorld ( true ) ; this . to . pose . updateWorld ( true ) ; let i , fLen , tLen , len , lnk , k , bFrom , bTo ; for ( [ k , bFrom ] of mapFrom . bones ) { bTo = mapTo . bones . get ( k ) ; if ( ! bTo ) { console . warn ( 'Target missing bone :' , k ) ; continue ; } if ( bFrom instanceof BoneInfo && bTo instanceof BoneInfo ) { lnk = new BoneLink ( bFrom . index , bFrom . name , bTo . index , bTo . name ) ; lnk . bind ( this . from . pose , this . to . pose ) ; this . map . set ( k , lnk ) ; } else if ( bFrom instanceof BoneChain && bTo instanceof BoneChain ) { fLen = bFrom . items . length ; tLen = bTo . items . length ; if ( fLen == 1 && tLen == 1 ) { this . map . set ( k , new BoneLink ( bFrom . items [ 0 ] . index , bFrom . items [ 0 ] . name , bTo . items [ 0 ] . index , bTo . items [ 0 ] . name ) . bind ( this . from . pose , this . to . pose ) ) ; } else if ( fLen >= 2 && tLen >= 2 ) { this . map . set ( k + "_0" , new BoneLink ( bFrom . items [ 0 ] . index , bFrom . items [ 0 ] . name , bTo . items [ 0 ] . index , bTo . items [ 0 ] . name ) . bind ( this . from . pose , this . to . pose ) ) ; this . map . set ( k + "_x" , new BoneLink ( bFrom . items [ fLen - 1 ] . index , bFrom . items [ fLen - 1 ] . name , bTo . items [ tLen - 1 ] . index , bTo . items [ tLen - 1 ] . name ) . bind ( this . from . pose , this . to . pose ) ) ; for ( i = 1 ; i < Math . min ( fLen - 1 , tLen - 1 ) ; i ++ ) { lnk = new BoneLink ( bFrom . items [ i ] . index , bFrom . items [ i ] . name , bTo . items [ i ] . index , bTo . items [ i ] . name ) ; lnk . bind ( this . from . pose , this . to . pose ) ; this . map . set ( k + "_" + i , lnk ) ; } } else { len = Math . min ( bFrom . items . length , bTo . items . length ) ; for ( i = 0 ; i < len ; i ++ ) { lnk = new BoneLink ( bFrom . items [ i ] . index , bFrom . items [ i ] . name , bTo . items [ i ] . index , bTo . items [ i ] . name ) ; lnk . bind ( this . from . pose , this . to . pose ) ; this . map . set ( k + "_" + i , lnk ) ; } } } else { console . warn ( 'Bone Mapping is mix match of info and chain' , k ) ; } } const hip = this . map . get ( 'hip' ) ; if ( hip ) { const fBone = this . from . pose . bones [ hip . fromIndex ] ; const tBone = this . to . pose . bones [ hip . toIndex ] ; Vec3Util . nearZero ( this . from . posHip , fBone . world . pos ) ; Vec3Util . nearZero ( this . to . posHip , tBone . world . pos ) ; this . hipScale = Math . abs ( this . to . posHip [ 1 ] / this . from . posHip [ 1 ] ) ; } return true ; } animateNext ( dt ) { this . anim . update ( dt ) . applyPose ( this . from . pose ) ; this . from . pose . updateWorld ( true ) ; this . applyRetarget ( ) ; this . to . pose . updateWorld ( true ) ; return this ; } atKey ( k ) { this . anim . atKey ( k ) . applyPose ( this . from . pose ) ; this . from . pose . updateWorld ( true ) ; this . applyRetarget ( ) ; this . to . pose . updateWorld ( true ) ; return this ; } applyRetarget ( ) { const fPose = this . from . pose . bones ; const tPose = this . to . pose . bones ; const diff = quat . create ( ) ; const tmp = quat . create ( ) ; let fBone ; let tBone ; let bl ; for ( bl of this . map . values ( ) ) { fBone = fPose [ bl . fromIndex ] ; tBone = tPose [ bl . toIndex ] ; quat . mul ( diff , bl . quatFromParent , fBone . local . rot ) ; if ( quat . dot ( diff , bl . quatDotCheck ) < 0 ) { QuatUtil . negate ( tmp , bl . wquatFromTo ) ; quat . mul ( diff , diff , tmp ) ; } else { quat . mul ( diff , diff , bl . wquatFromTo ) ; } quat . mul ( diff , bl . toWorldLocal , diff ) ; quat . copy ( tBone . local . rot , diff ) ; } const hip = this . map . get ( 'hip' ) ; if ( hip ) { const fBone = this . from . pose . bones [ hip . fromIndex ] ; const tBone = this . to . pose . bones [ hip . toIndex ] ; const v = vec3 . create ( ) ; vec3 . sub ( v , fBone . world . pos , this . from . posHip ) ; vec3 . scale ( v , v , this . hipScale ) ; vec3 . add ( v , v , this . to . posHip ) ; vec3 . copy ( tBone . local . pos , v ) ; } } } export default Retarget ;