private bool GetPatchAtPath(TargetPath path, out IPatchable patch) { var targetId = TargetMap[path.Placeholder]; if (path.AnimatibleType == "actor") { // pull current value var actor = (Actor)manager.App.FindActor(targetId); ActorPatch actorPatch = (ActorPatch)manager.AnimInputPatches.GetOrCreate(targetId, () => new ActorPatch(targetId)); if (actor?.GeneratePatch(actorPatch, path) != null) { patch = actorPatch; return(true); } } patch = null; return(false); }
internal void Interpolate( ActorPatch finalFrame, string animationName, float duration, float[] curve, bool enabled) { // Ensure duration is in range [0...n]. duration = Math.Max(0, duration); const int FPS = 10; float timeStep = duration / FPS; // If the curve is malformed, fall back to linear. if (curve.Length != 4) { curve = new float[] { 0, 0, 1, 1 }; } // Are we patching the transform? bool animateTransform = finalFrame.Transform != null && finalFrame.Transform.Local != null && finalFrame.Transform.Local.IsPatched(); var finalTransform = finalFrame.Transform.Local; // What parts of the transform are we animating? bool animatePosition = animateTransform && finalTransform.Position != null && finalTransform.Position.IsPatched(); bool animateRotation = animateTransform && finalTransform.Rotation != null && finalTransform.Rotation.IsPatched(); bool animateScale = animateTransform && finalTransform.Scale != null && finalTransform.Scale.IsPatched(); // Ensure we have a well-formed rotation quaternion. for (; animateRotation;) { var rotation = finalTransform.Rotation; bool hasAllComponents = rotation.X.HasValue && rotation.Y.HasValue && rotation.Z.HasValue && rotation.W.HasValue; // If quaternion is incomplete, fall back to the identity. if (!hasAllComponents) { finalTransform.Rotation = new QuaternionPatch(Quaternion.identity); break; } // Ensure the quaternion is normalized. var lengthSquared = (rotation.X.Value * rotation.X.Value) + (rotation.Y.Value * rotation.Y.Value) + (rotation.Z.Value * rotation.Z.Value) + (rotation.W.Value * rotation.W.Value); if (lengthSquared == 0) { // If the quaternion is length zero, fall back to the identity. finalTransform.Rotation = new QuaternionPatch(Quaternion.identity); break; } else if (lengthSquared != 1.0f) { // If the quaternion length is not 1, normalize it. var inverseLength = 1.0f / Mathf.Sqrt(lengthSquared); rotation.X *= inverseLength; rotation.Y *= inverseLength; rotation.Z *= inverseLength; rotation.W *= inverseLength; } break; } // Create the sampler to calculate ease curve values. var sampler = new CubicBezier(curve[0], curve[1], curve[2], curve[3]); var keyframes = new List <MWAnimationKeyframe>(); // Generate keyframes float currTime = 0; do { var keyframe = NewKeyframe(currTime); var unitTime = duration > 0 ? currTime / duration : 1; BuildKeyframe(keyframe, unitTime); keyframes.Add(keyframe); currTime += timeStep; }while (currTime <= duration && timeStep > 0); // Final frame (if needed) if (currTime - duration > 0) { var keyframe = NewKeyframe(duration); BuildKeyframe(keyframe, 1); keyframes.Add(keyframe); } // Create and optionally start the animation. CreateAnimation( animationName, keyframes, events: null, wrapMode: MWAnimationWrapMode.Once, initialState: new MWSetAnimationStateOptions { Enabled = enabled }, isInternal: true, managed: false, onCreatedCallback: null); bool LerpFloat(out float dest, float start, float?end, float t) { if (end.HasValue) { dest = Mathf.LerpUnclamped(start, end.Value, t); return(true); } dest = 0; return(false); } bool SlerpQuaternion(out Quaternion dest, Quaternion start, QuaternionPatch end, float t) { if (end != null) { dest = Quaternion.SlerpUnclamped(start, new Quaternion(end.X.Value, end.Y.Value, end.Z.Value, end.W.Value), t); return(true); } dest = Quaternion.identity; return(false); } void BuildKeyframePosition(MWAnimationKeyframe keyframe, float t) { float value; if (LerpFloat(out value, transform.localPosition.x, finalTransform.Position.X, t)) { keyframe.Value.Transform.Local.Position.X = value; } if (LerpFloat(out value, transform.localPosition.y, finalTransform.Position.Y, t)) { keyframe.Value.Transform.Local.Position.Y = value; } if (LerpFloat(out value, transform.localPosition.z, finalTransform.Position.Z, t)) { keyframe.Value.Transform.Local.Position.Z = value; } } void BuildKeyframeScale(MWAnimationKeyframe keyframe, float t) { float value; if (LerpFloat(out value, transform.localScale.x, finalTransform.Scale.X, t)) { keyframe.Value.Transform.Local.Scale.X = value; } if (LerpFloat(out value, transform.localScale.y, finalTransform.Scale.Y, t)) { keyframe.Value.Transform.Local.Scale.Y = value; } if (LerpFloat(out value, transform.localScale.z, finalTransform.Scale.Z, t)) { keyframe.Value.Transform.Local.Scale.Z = value; } } void BuildKeyframeRotation(MWAnimationKeyframe keyframe, float t) { Quaternion value; if (SlerpQuaternion(out value, transform.localRotation, finalTransform.Rotation, t)) { keyframe.Value.Transform.Local.Rotation = new QuaternionPatch(value); } } void BuildKeyframe(MWAnimationKeyframe keyframe, float unitTime) { float curveTime = sampler.Sample(unitTime); if (animatePosition) { BuildKeyframePosition(keyframe, curveTime); } if (animateRotation) { BuildKeyframeRotation(keyframe, curveTime); } if (animateScale) { BuildKeyframeScale(keyframe, curveTime); } } MWAnimationKeyframe NewKeyframe(float time) { var keyframe = new MWAnimationKeyframe { Time = time, Value = new ActorPatch() }; if (animateTransform) { keyframe.Value.Transform = new ActorTransformPatch() { Local = new ScaledTransformPatch() }; } if (animatePosition) { keyframe.Value.Transform.Local.Position = new Vector3Patch(); } if (animateRotation) { keyframe.Value.Transform.Local.Rotation = new QuaternionPatch(); } if (animateScale) { keyframe.Value.Transform.Local.Scale = new Vector3Patch(); } return(keyframe); } }
internal ActorChangedEvent(Guid actorId, ActorPatch actor) : base(actorId) { _actor = actor; }