private void UpdateTimer(object sender, EventArgs e) { if (Mode == AnimationMode.Spin) { if (MoveMultiplier < 1.0f) { MoveMultiplier += _moveAcceleration; } else { MoveMultiplier = 1.0f; } } else if (Mode != AnimationMode.Idle) { _autoMoveCurrent += _autoMoveStep; // Allow timer to overflow for 2 subsequent frames, so we // get finalizing 1.0 value at all times if (_autoMoveCurrent >= 1.0f + (_autoMoveStep * 2)) { Stop(true); } else { var newMultiplier = MathC.SmoothStep(0.0, 1.0, _autoMoveCurrent); MoveMultiplier = (float)MathC.Clamp(newMultiplier, 0.0, 1.0); } } else { Stop(true); } }
public void UpdateTransform(int meshIndex, Vector3 newRot, Vector3 newPos) { if (CurrentAnim == null || CurrentKeyFrame == null) { return; } // Backup everything and push undo on first occurence of editing if (!MadeChanges || _backupPos.Count == 0 || _backupRot.Count == 0) { _initialPos = CurrentKeyFrame.Translations[0]; _initialRot = CurrentKeyFrame.Rotations[meshIndex]; _backupPos.Clear(); _backupRot.Clear(); ActiveFrames.ForEach(f => { _backupRot.Add(f.Rotations[meshIndex]); _backupPos.Add(f.Translations[0]); }); Tool.UndoManager.PushAnimationChanged(this, CurrentAnim); MadeChanges = true; } // Calculate deltas for other frames processing var deltaPos = newPos - _initialPos; var deltaRot = newRot - _initialRot; // Define animation properties bool evolve = TransformMode != AnimTransformMode.Simple && ActiveFrames.Count > 1; bool smooth = TransformMode == AnimTransformMode.Smooth || TransformMode == AnimTransformMode.SmoothReverse || TransformMode == AnimTransformMode.Symmetric; bool reverse = TransformMode == AnimTransformMode.LinearReverse || TransformMode == AnimTransformMode.SmoothReverse; bool loop = TransformMode == AnimTransformMode.Symmetric || TransformMode == AnimTransformMode.SymmetricLinear; // Calculate evolution float frameCount = loop || reverse ? Selection.Y - Selection.X : CurrentFrameIndex - Selection.X; float currentStep = 0; int index = 0; foreach (var keyframe in ActiveFrames) { float midFrame = loop ? CurrentFrameIndex - Selection.X : (reverse ? CurrentFrameIndex : frameCount); float bias = (currentStep <= midFrame) ? (reverse ? 1.0f : currentStep / midFrame) : (frameCount - currentStep) / (frameCount - midFrame); // Single-pass smoothstep doesn't look organic on fast animations, hence we're using 2-pass smootherstep here. float weight = smooth ? (float)MathC.SmoothStep(0, 1, MathC.SmoothStep(0, 1, bias)) : bias; // Apply deltas to backed-up transforms var currPos = _backupPos[index] + deltaPos; var currRot = _backupRot[index] + deltaRot; var translationVector = currPos; if (evolve) { translationVector = Vector3.Lerp(_backupPos[index], translationVector, weight); } // Foolproof stuff in case user hardly messes with transform during playback... if (float.IsNaN(translationVector.X)) { translationVector.X = _backupPos[index].X; } if (float.IsNaN(translationVector.Y)) { translationVector.Y = _backupPos[index].Y; } if (float.IsNaN(translationVector.Z)) { translationVector.Z = _backupPos[index].Z; } keyframe.Translations[0] = translationVector; keyframe.TranslationsMatrices[0] = Matrix4x4.CreateTranslation(translationVector); var rotVector = currRot; Quaternion finalQuat; if (evolve) { // Calculate source and destination quats and decide on direction based on dot product. var srcQuat = Quaternion.CreateFromYawPitchRoll(_backupRot[index].Y, _backupRot[index].X, _backupRot[index].Z); var destQuat = Quaternion.CreateFromYawPitchRoll(currRot.Y, currRot.X, currRot.Z); if (Quaternion.Dot(srcQuat, destQuat) < 0) { Quaternion.Negate(srcQuat); } finalQuat = Quaternion.Lerp(srcQuat, destQuat, weight); } else { finalQuat = Quaternion.CreateFromYawPitchRoll(rotVector.Y, rotVector.X, rotVector.Z); } // We're not converting quat to rotations because we have to check-up for NaNs var rotationVector = MathC.QuaternionToEuler(finalQuat); // Foolproof stuff in case user hardly messes with transform during playback... if (float.IsNaN(rotationVector.X)) { rotationVector.X = _backupRot[index].X; } if (float.IsNaN(rotationVector.Y)) { rotationVector.Y = _backupRot[index].Y; } if (float.IsNaN(rotationVector.Z)) { rotationVector.Z = _backupRot[index].Z; } // NaNs filtered out, now we can put actual data. keyframe.Quaternions[meshIndex] = Quaternion.CreateFromYawPitchRoll(rotationVector.Y, rotationVector.X, rotationVector.Z); keyframe.Rotations[meshIndex] = rotationVector; index++; currentStep++; if (currentStep > frameCount) { currentStep = frameCount; } } }