import { AnimationTrack } from "./tracks/AnimationTrack" ; import { SerializableObject } from "../core/SerializableObject" ; import { Visual } from "../graphics/Visual" ; import { Entity , EntityInternal } from "../core/Entity" ; import { Material } from "../graphics/Material" ; import { Animation } from "./Animation" ; import { AnimationTargets , AnimationInstance } from "./AnimationInstance" ; import { AnimationTrackDefinition } from "./AnimationTrackDefinition" ; import { MathEx } from "../math/MathEx" ; import { IPlayAnimationOptions } from "./AnimationTypes" ; namespace Private { export function evaluateProperty ( obj , tokens , currentToken , track , time , handler ) { if ( currentToken === tokens . length - 1 ) { if ( obj ) { const value = track . getSample ( time , obj [ tokens [ currentToken ] ] ) ; if ( value !== undefined ) { handler ( obj , tokens [ currentToken ] , value ) ; } } } else { if ( obj ) { const subProperty = tokens [ currentToken ] ; if ( obj . constructor . name === "Visual" && subProperty === "_material" ) { const visual = obj as Visual ; let animatedMaterial = visual . animatedMaterial as Material ; if ( ! animatedMaterial && visual . material ) { animatedMaterial = visual . material . copy ( ) as Material ; visual . animatedMaterial = animatedMaterial ; } Private . evaluateProperty ( animatedMaterial , tokens , currentToken + 2 , track , time , handler ) ; } else { Private . evaluateProperty ( obj [ subProperty ] , tokens , currentToken + 1 , track , time , handler ) ; } } } } } export class AnimationUtils { static evaluateTrack ( track , propertyPath , entity , time , handler ) { let tokens = track . tokenCache [ propertyPath ] ; if ( ! tokens ) { tokens = propertyPath . split ( "/" ) ; track . tokenCache [ propertyPath ] = tokens ; } if ( tokens . length > 1 ) { const typeName = tokens [ 0 ] ; const component = EntityInternal . getComponentByName ( entity , typeName ) ; if ( component ) { Private . evaluateProperty ( component , tokens , 1 , track , time , ( target , prop , value ) => handler ( target , prop , value ) ) ; } } } static applyTrack ( track , entity , evalTime , playTime ) { AnimationUtils . evaluateTrack ( track . track . instance as AnimationTrack , track . propertyPath , entity , evalTime , ( target , prop , value ) => { if ( track . transition ) { if ( playTime < track . transition . duration ) { const blendFactor = playTime / track . transition . duration ; target [ prop ] = track . transition . blend ( track . transition . sourceValue , value , blendFactor ) ; } else { target [ prop ] = track . transition . blend ( track . transition . sourceValue , value , 1 ) ; delete track . transition ; } } else { target [ prop ] = value ; } } ) ; } static evaluateAnimation ( animation , rootTarget , handler , targets ? ) { for ( const track of animation . tracks . data ) { if ( ! track . track . instance ) { continue ; } let target = rootTarget ; if ( track . targetName ) { if ( targets ) { target = targets [ track . targetName ] ; } else { target = rootTarget . name === track . targetName ? rootTarget : rootTarget . findChild ( track . targetName ) ; } if ( ! target ) { continue ; } } handler ( track , target ) ; } } static applyAnimation ( animation , rootTarget , evalTime , playTime , targets ? ) { AnimationUtils . evaluateAnimation ( animation , rootTarget , ( track , target ) => { AnimationUtils . applyTrack ( track , target , evalTime , playTime ) ; } , targets ) ; } static playAnimation ( owner , anim , options ? ) { AnimationUtils . playAnimationInstance ( anim , options ) ; AnimationUtils . fetchTargetsIfNecessary ( owner , anim ) ; AnimationUtils . resetAnimationTransition ( anim ) ; } static playAnimationInstance ( anim , options ? ) { const duration = ( anim . animation as Animation ) . duration ; if ( options && options . startTime !== undefined ) { anim . localTime = duration * MathEx . clamp ( options . startTime , 0 , 1 ) ; } else { if ( anim . localTime >= duration ) { anim . localTime = 0 ; } } anim . isPlaying = true ; anim . playCount = 0 ; anim . playTime = 0 ; } static fetchTargetsIfNecessary ( entity , anim ) { if ( anim . targets ) { return ; } anim . targets = { } ; for ( const a of ( anim . animation as Animation ) . tracks . data ) { if ( a . targetName ) { const target = entity . name === a . targetName ? entity : entity . findChild ( a . targetName ) ; if ( target ) { anim . targets [ a . targetName ] = target ; } } } } static resetAnimationTransition ( anim ) { for ( const a of ( anim . animation as Animation ) . tracks . data ) { delete a . transition ; } } }