// 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 { // debug going through animation tree data.AddVisitedTreeNodesPathPoint(-1); // we will go back to parent // m_lastKnownFrameCounter = data.Controller.FrameCounter; // do NOT set last known frame counter here, this is invalid state return; } float parameterValue = ComputeParamValue(ref data); 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) { // debug going through animation tree data.AddVisitedTreeNodesPathPoint(1); // we will go to first child ChildMappings[0].Child.Update(ref data); PushLocalTimeToSlaves(0); } 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) { // debug going through animation tree data.AddVisitedTreeNodesPathPoint(ChildMappings.Count - 1 + 1); // we will go to last child ChildMappings[index1].Child.Update(ref data); PushLocalTimeToSlaves(index1); } 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; } if (t < 0.001f) { t = 0.0f; } else if (t > 0.999f) { t = 1.0f; } var child1 = ChildMappings[index1].Child; var child2 = ChildMappings[index2].Child; // first (its weight > 0.5) node, dominant if (child1 != null && t < 1.0f) { // debug going through animation tree data.AddVisitedTreeNodesPathPoint(index1 + 1); // we will go to dominant child child1.Update(ref data); PushLocalTimeToSlaves(index1); } else { data.BonesResult = data.Controller.ResultBonesPool.Alloc(); } // second (its weight < 0.5) node MyAnimationUpdateData animationUpdateData2 = data; // local copy for second one if (child2 != null && t > 0.0f) { animationUpdateData2.DeltaTimeInSeconds = 0.0; // driven by dominant child 1, do not change you time yourself // debug going through animation tree animationUpdateData2.AddVisitedTreeNodesPathPoint(index2 + 1); // we will go to dominant child child2.Update(ref animationUpdateData2); data.VisitedTreeNodesCounter = animationUpdateData2.VisitedTreeNodesCounter; } 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); } // debug going through animation tree m_lastKnownFrameCounter = data.Controller.FrameCounter; data.AddVisitedTreeNodesPathPoint(-1); // 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 if (!ProcessLayerTimeSync(ref data) && m_timeAdvancedOnFrameNum != data.Controller.FrameCounter) // time advances only if it has not already advanced { m_timeAdvancedOnFrameNum = data.Controller.FrameCounter; m_localTime += data.DeltaTimeInSeconds * Speed; if (m_loop) { while (m_localTime >= m_animationClip.Duration) m_localTime -= m_animationClip.Duration; while (m_localTime < 0) m_localTime += m_animationClip.Duration; } else { if (m_localTime >= m_animationClip.Duration) { m_localTime = m_animationClip.Duration; } else if (m_localTime < 0) { m_localTime = 0; } } } // 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 || charBoneIndex >= data.BonesResult.Count || 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) } } } // debug going through animation tree 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 if (!ProcessLayerTimeSync(ref data) && m_timeAdvancedOnFrameNum != data.Controller.FrameCounter) // time advances only if it has not already advanced { m_timeAdvancedOnFrameNum = data.Controller.FrameCounter; m_localTime += data.DeltaTimeInSeconds * Speed; if (m_loop) { while (m_localTime >= m_animationClip.Duration) { m_localTime -= m_animationClip.Duration; } while (m_localTime < 0) { m_localTime += m_animationClip.Duration; } } else { if (m_localTime >= m_animationClip.Duration) { m_localTime = m_animationClip.Duration; } else if (m_localTime < 0) { m_localTime = 0; } } } // 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 || charBoneIndex >= data.BonesResult.Count || 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) } } } // debug going through animation tree data.AddVisitedTreeNodesPathPoint(-1); // finishing this node, we will go back to parent }
// 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 { // debug going through animation tree data.AddVisitedTreeNodesPathPoint(-1); // we will go back to parent // m_lastKnownFrameCounter = data.Controller.FrameCounter; // do NOT set last known frame counter here, this is invalid state return; } float parameterValue = ComputeParamValue(ref data); 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) { // debug going through animation tree data.AddVisitedTreeNodesPathPoint(1); // we will go to first child ChildMappings[0].Child.Update(ref data); PushLocalTimeToSlaves(0); } 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) { // debug going through animation tree data.AddVisitedTreeNodesPathPoint(ChildMappings.Count - 1 + 1); // we will go to last child ChildMappings[index1].Child.Update(ref data); PushLocalTimeToSlaves(index1); } 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; } if (t < 0.001f) t = 0.0f; else if (t > 0.999f) t = 1.0f; var child1 = ChildMappings[index1].Child; var child2 = ChildMappings[index2].Child; // first (its weight > 0.5) node, dominant if (child1 != null && t < 1.0f) { // debug going through animation tree data.AddVisitedTreeNodesPathPoint(index1 + 1); // we will go to dominant child child1.Update(ref data); PushLocalTimeToSlaves(index1); } else data.BonesResult = data.Controller.ResultBonesPool.Alloc(); // second (its weight < 0.5) node MyAnimationUpdateData animationUpdateData2 = data; // local copy for second one if (child2 != null && t > 0.0f) { animationUpdateData2.DeltaTimeInSeconds = 0.0; // driven by dominant child 1, do not change you time yourself // debug going through animation tree animationUpdateData2.AddVisitedTreeNodesPathPoint(index2 + 1); // we will go to dominant child child2.Update(ref animationUpdateData2); data.VisitedTreeNodesCounter = animationUpdateData2.VisitedTreeNodesCounter; } 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); } // debug going through animation tree m_lastKnownFrameCounter = data.Controller.FrameCounter; data.AddVisitedTreeNodesPathPoint(-1); // we will go back to parent }