//---------------------------------------------- // U P D A T E M O D E L A N I M A T I O N S //---------------------------------------------- ///<summary> Gets the animation frame (based on elapsed time) for all nodes and loads them into the model node transforms. </summary> private void UpdateModelAnimations(GameTime gameTime) { if (animations.Count <= 0 || currentAnim >= animations.Count) { return; } AnimationTimeLogic(gameTime); // process what to do based on animation time (frames, duration, complete | looping) int cnt = animations[currentAnim].animatedNodes.Count; // loop thru animated nodes for (int n = 0; n < cnt; n++) { AnimNodes animNode = animations[currentAnim].animatedNodes[n]; // get animation keyframe lists (each animNode) ModelNode node = animNode.nodeRef; // get bone associated with this animNode (could be mesh-node) node.local_mtx = animations[currentAnim].Interpolate(currentAnimFrameTime, animNode); // interpolate keyframes (animate local matrix) for this bone } }
public List <AnimNodes> animatedNodes; // holds the animated nodes // I N T E R P O L A T E ///<summary> animation blending between key-frames </summary> public Matrix Interpolate(double animTime, AnimNodes nodeAnim) { var durationSecs = DurationInSeconds + DurationInSecondsAdded; while (animTime > durationSecs) // If the requested play-time is past the end of the animation, loop it (ie: time = 20 but duration = 16 so time becomes 4) { animTime -= durationSecs; } Quaternion q1 = nodeAnim.qrot[0], q2 = q1; // init rot as entry 0 for both keys (init may be needed cuz conditional value assignment can upset compiler) Vector3 p1 = nodeAnim.position[0], p2 = p1; // " pos Vector3 s1 = nodeAnim.scale[0], s2 = s1; // " scale double tq1 = nodeAnim.qrotTime[0], tq2 = tq1; // " rot-time double tp1 = nodeAnim.positionTime[0], tp2 = tp1; // " pos-time double ts1 = nodeAnim.scaleTime[0], ts2 = ts1; // " scale-time // GET ROTATION KEYFRAMES int end_t_index = nodeAnim.qrotTime.Count - 1; // final time's index (starting with qrot cuz we do it first - we'll cahnge this variable for pos and scale) int end_index = nodeAnim.qrot.Count - 1; // final rot frame var end_time = nodeAnim.qrotTime[end_t_index]; // get final rotation-time if (animTime > end_time) { // if animTime is past final rotation-time: Set to interpolate between last and first frames (for animation-loop) tq1 = end_time; // key 1 time is last keyframe and time 2 is time taken after to get to frame 0 (see below) tq2 += durationSecs; // total duration accounting for time to loop from last frame to frame 0 (with DurationInSecondsAdded) q1 = nodeAnim.qrot[end_index]; // get final quaternion (count-1), NOTE: q2 already set above (key frame 0) } else { int frame2 = end_index, frame1; // animTime t = frame2 for (; frame2 > -1; frame2--) // loop from last index to 0 (until find correct place on timeline): { var t = nodeAnim.qrotTime[frame2]; // what's the time at this frame? if (t < animTime) { break; // if the current_time > the frame time then we've found the spot we're looking for (break out) } } if (frame2 < end_index) { frame2++; // at this point the frame2 is 1 less than what we're looking for so add 1 } q2 = nodeAnim.qrot[frame2]; tq2 = nodeAnim.qrotTime[frame2]; frame1 = frame2 - 1; if (frame1 < 0) { frame1 = end_index; // loop frame1 to last frame tq1 = nodeAnim.qrotTime[frame1] - durationSecs; // Using: frame2time - frame1time, so we need time1 to be less _ thus: subtract durationSecs to fix it } else { tq1 = nodeAnim.qrotTime[frame1]; // get time1 } q1 = nodeAnim.qrot[frame1]; } // GET POSITION KEY FRAMES end_t_index = nodeAnim.positionTime.Count - 1; // final time's index end_index = nodeAnim.position.Count - 1; // final pos frame end_time = nodeAnim.positionTime[end_t_index]; // get final position-time if (animTime > end_time) // if animTime is past final pos-time: Set to interpolate between last and first frames (for animation-loop) { tp1 = end_time; // key 1 time is last keyframe and time 2 is time taken after to get to frame 0 (see below) tp2 += durationSecs; // total duration accounting for time to loop from last frame to frame 0 (with DurationInSecondsAdded) p1 = nodeAnim.position[end_index]; // get final position (count-1), NOTE: q2 already set above (key frame 0) } else { int frame2 = end_index, frame1; for (; frame2 > -1; frame2--) // loop from last index to 0 (until find correct place on timeline): { var t = nodeAnim.positionTime[frame2]; // what's the time at this frame? if (t < animTime) { break; // if the current_time > the frame time then we've found the spot we're looking for (break out) } } if (frame2 < end_index) { frame2++; // at this point the frame2 is 1 less than what we're looking for so add 1 } p2 = nodeAnim.position[frame2]; tp2 = nodeAnim.positionTime[frame2]; frame1 = frame2 - 1; if (frame1 < 0) { frame1 = end_index; // loop frame1 to last frame tp1 = nodeAnim.positionTime[frame1] - durationSecs; // Using: frame2time - frame1time, so we need time1 to be less _ thus: subtract durationSecs to fix it } else { tp1 = nodeAnim.positionTime[frame1]; // get time1 } p1 = nodeAnim.position[frame1]; } // GET SCALE KEYFRAMES end_t_index = nodeAnim.scaleTime.Count - 1; // final time's index end_index = nodeAnim.scale.Count - 1; // final scale frame end_time = nodeAnim.scaleTime[end_t_index]; // get final scale-time if (animTime > end_time) // if animTime is past final scale-time: Set to interpolate between last and first frames (for animation-loop) { ts1 = end_time; // key 1 time is last keyframe and time 2 is time taken after to get to frame 0 (see below) ts2 += durationSecs; // total duration accounting for time to loop from last frame to frame 0 (with DurationInSecondsAdded) s1 = nodeAnim.scale[end_index]; // get final scale (count-1), NOTE: q2 already set above (key frame 0) } else { int frame2 = end_index, frame1; for (; frame2 > -1; frame2--) // loop from last index to 0 (until find correct place on timeline): { var t = nodeAnim.scaleTime[frame2]; // what's the time at this frame? if (animTime > t) { break; // if the current_time > the frame time then we've found the spot we're looking for (break out) } } if (frame2 < end_index) { frame2++; // at this point the frame2 is 1 less than what we're looking for so add 1 } s2 = nodeAnim.scale[frame2]; ts2 = nodeAnim.scaleTime[frame2]; frame1 = frame2 - 1; if (frame1 < 0) { frame1 = end_index; // loop frame1 to last frame ts1 = nodeAnim.scaleTime[frame1] - durationSecs; // Using: frame2time - frame1time, so we need time1 to be less _ thus: subtract durationSecs to fix it } else { ts1 = nodeAnim.scaleTime[frame1]; // get time1 } s1 = nodeAnim.scale[frame1]; } float tqi = 0, tpi = 0, tsi = 0; Quaternion q; tqi = (float)GetInterpolateTimePercent(tq1, tq2, animTime); // get the time% (0-1) q = Quaternion.Slerp(q1, q2, tqi); // blend the rotation between keys using the time percent Vector3 p; tpi = (float)GetInterpolateTimePercent(tp1, tp2, animTime); // " p = Vector3.Lerp(p1, p2, tpi); Vector3 s; tsi = (float)GetInterpolateTimePercent(ts1, ts2, animTime); // " s = Vector3.Lerp(s1, s2, tsi); var ms = Matrix.CreateScale(s); var mr = Matrix.CreateFromQuaternion(q); var mt = Matrix.CreateTranslation(p); var m = ms * mr * mt; // S,R,T return(m); } // Interpolate