/// <summary> /// The keys of each BabylonMorphTarget animation ARE NOT assumed to be identical. /// This function merges together all keys and binds to each an influence value for all targets. /// A target influence value is automatically computed when necessary. /// Computation rules are: /// - linear interpolation between target key range /// - constant value outside target key range /// </summary> /// <example> /// When: /// animation1.keys = {0, 25, 50, 100} /// animation2.keys = {50, 75, 100} /// /// Gives: /// mergedKeys = {0, 25, 50, 100, 75} /// range1=[0, 100] /// range2=[50, 100] /// for animation1, the value associated to key=75 is the interpolation of its values between 50 and 100 /// for animation2, the value associated to key=0 is equal to the one at key=50 since 0 is out of range [50, 100] (same for key=25)</example> /// <param name="babylonMorphTargetManager"></param> /// <returns>A map which for each frame, gives the influence value of all targets</returns> private Dictionary <int, List <float> > _getTargetManagerAnimationsData(BabylonMorphTargetManager babylonMorphTargetManager) { // Merge all keys into a single set (no duplicated frame) var mergedFrames = new HashSet <int>(); foreach (var babylonMorphTarget in babylonMorphTargetManager.targets) { if (babylonMorphTarget.animations != null) { var animation = babylonMorphTarget.animations[0]; foreach (BabylonAnimationKey animationKey in animation.keys) { mergedFrames.Add(animationKey.frame); } } } // For each frame, gives the influence value of all targets (gltf structure) var influencesPerFrame = new Dictionary <int, List <float> >(); foreach (var frame in mergedFrames) { influencesPerFrame.Add(frame, new List <float>()); } foreach (var babylonMorphTarget in babylonMorphTargetManager.targets) { // For a given target, for each frame, gives the influence value of the target (babylon structure) var influencePerFrameForTarget = new Dictionary <int, float>(); if (babylonMorphTarget.animations != null && babylonMorphTarget.animations.Length > 0) { var animation = babylonMorphTarget.animations[0]; if (animation.keys.Length == 1) { // Same influence for all frames var influence = animation.keys[0].values[0]; foreach (var frame in mergedFrames) { influencePerFrameForTarget.Add(frame, influence); } } else { // Retreive target animation key range [min, max] var babylonAnimationKeys = new List <BabylonAnimationKey>(animation.keys); babylonAnimationKeys.Sort(); var minAnimationKey = babylonAnimationKeys[0]; var maxAnimationKey = babylonAnimationKeys[babylonAnimationKeys.Count - 1]; foreach (var frame in mergedFrames) { // Surround the current frame with closest keys available for the target BabylonAnimationKey lowerAnimationKey = minAnimationKey; BabylonAnimationKey upperAnimationKey = maxAnimationKey; foreach (BabylonAnimationKey animationKey in animation.keys) { if (lowerAnimationKey.frame < animationKey.frame && animationKey.frame <= frame) { lowerAnimationKey = animationKey; } if (frame <= animationKey.frame && animationKey.frame < upperAnimationKey.frame) { upperAnimationKey = animationKey; } } // In case the target has a key for this frame // or the current frame is out of target animation key range if (lowerAnimationKey.frame == upperAnimationKey.frame) { influencePerFrameForTarget.Add(frame, lowerAnimationKey.values[0]); } else { // Interpolate influence values var t = 1.0f * (frame - lowerAnimationKey.frame) / (upperAnimationKey.frame - lowerAnimationKey.frame); var influence = Tools.Lerp(lowerAnimationKey.values[0], upperAnimationKey.values[0], t); influencePerFrameForTarget.Add(frame, influence); } } } } else { // Target is not animated // Fill all frames with 0 foreach (var frame in mergedFrames) { influencePerFrameForTarget.Add(frame, 0); } } // Switch from babylon to gltf storage representation foreach (var frame in mergedFrames) { List <float> influences = influencesPerFrame[frame]; influences.Add(influencePerFrameForTarget[frame]); } } return(influencesPerFrame); }