protected override void ProcessImpl(float[] buffer, int offset, int count) { int numSamples = count / 2; if (Duration == 0.0) { return; } double step = 1.0 / SampleRate; for (int i = 0; i < numSamples; i++) { float r = (float)(time / Duration); // FadeIn const float fadeIn = 0.08f; if (r < fadeIn) { r = 1.0f - r / fadeIn; } else { r = Curve.Sample((r - fadeIn) / (1.0f - fadeIn)); } float sampleGain = 1.0f - Amount * (1.0f - r) * Mix; buffer[offset + i * 2 + 0] *= sampleGain; buffer[offset + i * 2 + 1] *= sampleGain; time += step; if (time > Duration) { time = 0; } } }
protected override void ProcessImpl(float[] buffer, int offset, int count) { int numSamples = count / 2; for (int i = 0; i < numSamples; i++) { float f = MathL.Abs(2.0f * ((float)m_currentSample / m_length) - 1.0f); f = easing.Sample(f); float freq = fmin + (fmax - fmin) * f; filter.SetLowPass(q, freq); float[] s = { buffer[i * 2], buffer[i * 2 + 1] }; filter.Process(s, 0, 2); // Apply slight mixing float addMix = 0.85f; //buffer[i * 2 + 0] = buffer[i * 2 + 0] * addMix + s[0] * (1.0f - addMix); //buffer[i * 2 + 1] = buffer[i * 2 + 1] * addMix + s[1] * (1.0f - addMix); buffer[i * 2 + 0] = MathL.Lerp(buffer[i * 2 + 0], s[0], Mix * addMix); buffer[i * 2 + 1] = MathL.Lerp(buffer[i * 2 + 1], s[1], Mix * addMix); m_currentSample++; m_currentSample %= m_length; } }
protected override void ProcessImpl(Span <float> buffer) { int numSamples = buffer.Length / 2; for (int i = 0; i < numSamples; i++) { float f = MathL.Abs(2.0f * ((float)m_currentSample / m_length) - 1.0f); f = easing.Sample(f); float freq = fmin + (fmax - fmin) * f; filter.SetLowPass(q, freq); float[] s = { buffer[i * 2], buffer[i * 2 + 1] }; filter.Process(s); float addMix = 0.85f; buffer[i * 2 + 0] = MathL.Lerp(buffer[i * 2 + 0], s[0], Mix * addMix); buffer[i * 2 + 1] = MathL.Lerp(buffer[i * 2 + 1], s[1], Mix * addMix); m_currentSample++; m_currentSample %= m_length; } }
protected override float Interp(float min, float max, float alpha) => m_curve.Sample(alpha) * (max - min) + min;
protected override int Interp(int min, int max, float alpha) => (int)(m_curve.Sample(alpha) * (max - min)) + min;
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); } }
/// <summary> /// Computes the maximum squared distance from a point to the curve using the current parameterization. /// </summary> protected FLOAT FindMaxSquaredError(int first, int last, CubicBezier curve, out int split) { List<VECTOR> pts = _pts; List<FLOAT> u = _u; int s = (last - first + 1) / 2; int nPts = last - first + 1; FLOAT max = 0; for (int i = 1; i < nPts; i++) { VECTOR v0 = pts[first + i]; VECTOR v1 = curve.Sample(u[i]); FLOAT d = VectorHelper.DistanceSquared(v0, v1); if (d > max) { max = d; s = i; } } // split at point of maximum error split = s + first; if (split <= first) split = first + 1; if (split >= last) split = last - 1; return max; }
/// <summary> /// Attempts to find a slightly better parameterization for u on the given curve. /// </summary> protected void Reparameterize(int first, int last, CubicBezier curve) { List<VECTOR> pts = _pts; List<FLOAT> u = _u; int nPts = last - first; for (int i = 1; i < nPts; i++) { VECTOR p = pts[first + i]; FLOAT t = u[i]; FLOAT ti = 1 - t; // Control vertices for Q' VECTOR qp0 = (curve.p1 - curve.p0) * 3; VECTOR qp1 = (curve.p2 - curve.p1) * 3; VECTOR qp2 = (curve.p3 - curve.p2) * 3; // Control vertices for Q'' VECTOR qpp0 = (qp1 - qp0) * 2; VECTOR qpp1 = (qp2 - qp1) * 2; // Evaluate Q(t), Q'(t), and Q''(t) VECTOR p0 = curve.Sample(t); VECTOR p1 = ((ti * ti) * qp0) + ((2 * ti * t) * qp1) + ((t * t) * qp2); VECTOR p2 = (ti * qpp0) + (t * qpp1); // these are the actual fitting calculations using http://en.wikipedia.org/wiki/Newton%27s_method // We can't just use .X and .Y because Unity uses lower-case "x" and "y". FLOAT num = ((VectorHelper.GetX(p0) - VectorHelper.GetX(p)) * VectorHelper.GetX(p1)) + ((VectorHelper.GetY(p0) - VectorHelper.GetY(p)) * VectorHelper.GetY(p1)); FLOAT den = (VectorHelper.GetX(p1) * VectorHelper.GetX(p1)) + (VectorHelper.GetY(p1) * VectorHelper.GetY(p1)) + ((VectorHelper.GetX(p0) - VectorHelper.GetX(p)) * VectorHelper.GetX(p2)) + ((VectorHelper.GetY(p0) - VectorHelper.GetY(p)) * VectorHelper.GetY(p2)); FLOAT newU = t - num / den; if (Math.Abs(den) > EPSILON && newU >= 0 && newU <= 1) u[i] = newU; } }
public EffectParamF(float a, float b, CubicBezier curve) : base(a, b, (x, y, t) => curve.Sample(t) * (y - x) + x) { }
public EffectParamI(int a, int b, CubicBezier curve) : base(a, b, (x, y, t) => (int)(curve.Sample(t) * (y - x)) + x) { }