public abstract void Update(ref MyAnimationUpdateData data);
// Update animation node = compute bones positions and orientations from the track. public override void Update(ref MyAnimationUpdateData data) { // allocate result array from pool data.BonesResult = data.Controller.ResultBonesPool.Alloc(); if (m_animationClip != null && m_animationClip.Bones != null) { // if necessary, then rebuild bone indices mapping if (m_boneIndicesMapping == null) { RebuildBoneIndices(data.CharacterBones); Debug.Assert(m_boneIndicesMapping != null); } // advance local time m_localTime += data.DeltaTimeInSeconds * Speed; if (m_loop) { while (m_localTime >= m_animationClip.Duration) m_localTime -= m_animationClip.Duration; } else { if (m_localTime >= m_animationClip.Duration) { m_localTime = m_animationClip.Duration; } } // update indices of keyframes (for every bone) UpdateKeyframeIndices(); // ok, compute for every bone for (int i = 0; i < m_animationClip.Bones.Count; i++) { var currentBone = m_animationClip.Bones[i]; // two keyframe indices to be blended together (i, i+1) // blending N-1 to 0 if looped, staying on the last frame if not looped var currentKeyFrameIndex = m_currentKeyframes[i]; var currentKeyFrameIndex2 = currentKeyFrameIndex + 1; if (currentKeyFrameIndex2 >= currentBone.Keyframes.Count) // safety currentKeyFrameIndex2 = Math.Max(0, currentBone.Keyframes.Count - 1); int charBoneIndex = m_boneIndicesMapping[i]; // use mapping clip bone -> char bone if (charBoneIndex < 0 || data.LayerBoneMask[charBoneIndex] == false) // unaffected bone? continue; if (currentKeyFrameIndex != currentKeyFrameIndex2 && m_interpolate) { // interpolate between two keyframes var keyframe1 = currentBone.Keyframes[currentKeyFrameIndex]; var keyframe2 = currentBone.Keyframes[currentKeyFrameIndex2]; float t = (float)((m_localTime - keyframe1.Time) * keyframe2.InvTimeDiff); t = MathHelper.Clamp(t, 0, 1); Quaternion.Slerp(ref keyframe1.Rotation, ref keyframe2.Rotation, t, out data.BonesResult[charBoneIndex].Rotation); Vector3.Lerp(ref keyframe1.Translation, ref keyframe2.Translation, t, out data.BonesResult[charBoneIndex].Translation); } else if (currentBone.Keyframes.Count != 0) { // just copy keyframe, because currentKeyFrameIndex == currentKeyFrameIndex2 data.BonesResult[charBoneIndex].Rotation = currentBone.Keyframes[currentKeyFrameIndex].Rotation; data.BonesResult[charBoneIndex].Translation = currentBone.Keyframes[currentKeyFrameIndex].Translation; } else { // zero keyframe count -> rest pose, leave it be (it is there from allocation) } } } }
// --------------- methods (public) -------------------------------------------------- // Update animation node = compute bones positions and orientations from the track. public override void Update(ref MyAnimationUpdateData data) { // debug going through animation tree data.BonesResult = data.Controller.ResultBonesPool.Alloc(); data.AddVisitedTreeNodesPathPoint(-1); // finishing this node, we will go back to parent }
// Update animation node = compute bones positions and orientations from the track. public override void Update(ref MyAnimationUpdateData data) { // allocate result array from pool data.BonesResult = data.Controller.ResultBonesPool.Alloc(); if (m_animationClip != null && m_animationClip.Bones != null) { // if necessary, then rebuild bone indices mapping if (m_boneIndicesMapping == null) { RebuildBoneIndices(data.CharacterBones); Debug.Assert(m_boneIndicesMapping != null); } // advance local time m_localTime += data.DeltaTimeInSeconds * Speed; if (m_loop) { while (m_localTime >= m_animationClip.Duration) { m_localTime -= m_animationClip.Duration; } } else { if (m_localTime >= m_animationClip.Duration) { m_localTime = m_animationClip.Duration; } } // update indices of keyframes (for every bone) UpdateKeyframeIndices(); // ok, compute for every bone for (int i = 0; i < m_animationClip.Bones.Count; i++) { var currentBone = m_animationClip.Bones[i]; // two keyframe indices to be blended together (i, i+1) // blending N-1 to 0 if looped, staying on the last frame if not looped var currentKeyFrameIndex = m_currentKeyframes[i]; var currentKeyFrameIndex2 = currentKeyFrameIndex + 1; if (currentKeyFrameIndex2 >= currentBone.Keyframes.Count) // safety { currentKeyFrameIndex2 = Math.Max(0, currentBone.Keyframes.Count - 1); } int charBoneIndex = m_boneIndicesMapping[i]; // use mapping clip bone -> char bone if (charBoneIndex < 0 || data.LayerBoneMask[charBoneIndex] == false) // unaffected bone? { continue; } if (currentKeyFrameIndex != currentKeyFrameIndex2 && m_interpolate) { // interpolate between two keyframes var keyframe1 = currentBone.Keyframes[currentKeyFrameIndex]; var keyframe2 = currentBone.Keyframes[currentKeyFrameIndex2]; float t = (float)((m_localTime - keyframe1.Time) * keyframe2.InvTimeDiff); t = MathHelper.Clamp(t, 0, 1); Quaternion.Slerp(ref keyframe1.Rotation, ref keyframe2.Rotation, t, out data.BonesResult[charBoneIndex].Rotation); Vector3.Lerp(ref keyframe1.Translation, ref keyframe2.Translation, t, out data.BonesResult[charBoneIndex].Translation); } else if (currentBone.Keyframes.Count != 0) { // just copy keyframe, because currentKeyFrameIndex == currentKeyFrameIndex2 data.BonesResult[charBoneIndex].Rotation = currentBone.Keyframes[currentKeyFrameIndex].Rotation; data.BonesResult[charBoneIndex].Translation = currentBone.Keyframes[currentKeyFrameIndex].Translation; } else { // zero keyframe count -> rest pose, leave it be (it is there from allocation) } } } }
// Update this node. Mixes two nodes from the interval. public override void Update(ref MyAnimationUpdateData data) { // basic check, we do not expect these values to be null, but you never know... Debug.Assert(data.Controller != null && data.Controller.Variables != null); if (ChildMappings.Count == 0) // no child nodes, no result { return; } float parameterValue; data.Controller.Variables.GetValue(ParameterName, out parameterValue); int index1 = -1; for (int i = ChildMappings.Count - 1; i >= 0; i--) // we expect ChildMappings.Count to be less than 5, for loop is ok { if (ChildMappings[i].ParamValueBinding <= parameterValue) { index1 = i; break; } } if (index1 == -1) // simply copy first one { if (ChildMappings[0].Child != null) { ChildMappings[0].Child.Update(ref data); } else { data.BonesResult = data.Controller.ResultBonesPool.Alloc(); // else return bind pose } } else if (index1 == ChildMappings.Count - 1) // simply copy last one { if (ChildMappings[index1].Child != null) { ChildMappings[index1].Child.Update(ref data); } else { data.BonesResult = data.Controller.ResultBonesPool.Alloc(); // else return bind pose } } else // blend between two { int index2 = index1 + 1; float paramBindingDiff = ChildMappings[index2].ParamValueBinding - ChildMappings[index1].ParamValueBinding; float t = (parameterValue - ChildMappings[index1].ParamValueBinding) / paramBindingDiff; // division here, improve me! if (t > 0.5f) // dominant index will always be index1 { index1++; index2--; t = 1.0f - t; } var child1 = ChildMappings[index1].Child; var child2 = ChildMappings[index2].Child; // first (its weight > 0.5) node, dominant if (child1 != null) { child1.Update(ref data); } else { data.BonesResult = data.Controller.ResultBonesPool.Alloc(); } // second (its weight < 0.5) node MyAnimationUpdateData animationUpdateData2 = data; // local copy for second one if (child2 != null) { if (child1 != null) { animationUpdateData2.DeltaTimeInSeconds = 0.0; // driven by dominant child 1, do not change you time yourself child2.SetLocalTimeNormalized(child1.GetLocalTimeNormalized()); } child2.Update(ref animationUpdateData2); } else { animationUpdateData2.BonesResult = animationUpdateData2.Controller.ResultBonesPool.Alloc(); } // and now blend for (int j = 0; j < data.BonesResult.Count; j++) { if (data.LayerBoneMask[j]) // mix only bones affected by current layer { data.BonesResult[j].Rotation = Quaternion.Slerp(data.BonesResult[j].Rotation, animationUpdateData2.BonesResult[j].Rotation, t); data.BonesResult[j].Translation = Vector3.Lerp(data.BonesResult[j].Translation, animationUpdateData2.BonesResult[j].Translation, t); } } // and deallocate animationUpdateData2.BonesResult since we no longer need it data.Controller.ResultBonesPool.Free(animationUpdateData2.BonesResult); } }
// Update this node. Mixes two nodes from the interval. public override void Update(ref MyAnimationUpdateData data) { // basic check, we do not expect these values to be null, but you never know... Debug.Assert(data.Controller != null && data.Controller.Variables != null); if (ChildMappings.Count == 0) // no child nodes, no result return; float parameterValue; data.Controller.Variables.GetValue(ParameterName, out parameterValue); int index1 = -1; for (int i = ChildMappings.Count - 1; i >= 0; i--) // we expect ChildMappings.Count to be less than 5, for loop is ok if (ChildMappings[i].ParamValueBinding <= parameterValue) { index1 = i; break; } if (index1 == -1) // simply copy first one { if (ChildMappings[0].Child != null) ChildMappings[0].Child.Update(ref data); else data.BonesResult = data.Controller.ResultBonesPool.Alloc(); // else return bind pose } else if (index1 == ChildMappings.Count - 1) // simply copy last one { if (ChildMappings[index1].Child != null) ChildMappings[index1].Child.Update(ref data); else data.BonesResult = data.Controller.ResultBonesPool.Alloc(); // else return bind pose } else // blend between two { int index2 = index1 + 1; float paramBindingDiff = ChildMappings[index2].ParamValueBinding - ChildMappings[index1].ParamValueBinding; float t = (parameterValue - ChildMappings[index1].ParamValueBinding) / paramBindingDiff; // division here, improve me! if (t > 0.5f) // dominant index will always be index1 { index1++; index2--; t = 1.0f - t; } var child1 = ChildMappings[index1].Child; var child2 = ChildMappings[index2].Child; // first (its weight > 0.5) node, dominant if (child1 != null) child1.Update(ref data); else data.BonesResult = data.Controller.ResultBonesPool.Alloc(); // second (its weight < 0.5) node MyAnimationUpdateData animationUpdateData2 = data; // local copy for second one if (child2 != null) { if (child1 != null) { animationUpdateData2.DeltaTimeInSeconds = 0.0; // driven by dominant child 1, do not change you time yourself child2.SetLocalTimeNormalized(child1.GetLocalTimeNormalized()); } child2.Update(ref animationUpdateData2); } else animationUpdateData2.BonesResult = animationUpdateData2.Controller.ResultBonesPool.Alloc(); // and now blend for (int j = 0; j < data.BonesResult.Count; j++) { if (data.LayerBoneMask[j]) // mix only bones affected by current layer { data.BonesResult[j].Rotation = Quaternion.Slerp(data.BonesResult[j].Rotation, animationUpdateData2.BonesResult[j].Rotation, t); data.BonesResult[j].Translation = Vector3.Lerp(data.BonesResult[j].Translation, animationUpdateData2.BonesResult[j].Translation, t); } } // and deallocate animationUpdateData2.BonesResult since we no longer need it data.Controller.ResultBonesPool.Free(animationUpdateData2.BonesResult); } }