summaryrefslogtreecommitdiff
path: root/Runtime/Graphics/ParticleSystem/PolynomialCurve.h
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Graphics/ParticleSystem/PolynomialCurve.h')
-rw-r--r--Runtime/Graphics/ParticleSystem/PolynomialCurve.h229
1 files changed, 229 insertions, 0 deletions
diff --git a/Runtime/Graphics/ParticleSystem/PolynomialCurve.h b/Runtime/Graphics/ParticleSystem/PolynomialCurve.h
new file mode 100644
index 0000000..d9e7270
--- /dev/null
+++ b/Runtime/Graphics/ParticleSystem/PolynomialCurve.h
@@ -0,0 +1,229 @@
+#ifndef POLYONOMIAL_CURVE_H
+#define POLYONOMIAL_CURVE_H
+
+template<class T>
+class AnimationCurveTpl;
+typedef AnimationCurveTpl<float> AnimationCurve;
+class Vector2f;
+
+struct Polynomial
+{
+ static float EvalSegment (float t, const float* coeff)
+ {
+ return (t * (t * (t * coeff[0] + coeff[1]) + coeff[2])) + coeff[3];
+ }
+
+ float coeff[4];
+};
+
+// Smaller, optimized version
+struct OptimizedPolynomialCurve
+{
+ enum { kMaxPolynomialKeyframeCount = 3, kSegmentCount = kMaxPolynomialKeyframeCount-1, };
+
+ Polynomial segments[kSegmentCount];
+
+ float timeValue;
+ float velocityValue;
+
+ // Evaluate double integrated Polynomial curve.
+ // Example: position = EvaluateDoubleIntegrated (normalizedTime) * startEnergy^2
+ // Use DoubleIntegrate function to for example turn a force curve into a position curve.
+ // Expects that t is in the 0...1 range.
+ float EvaluateDoubleIntegrated (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+ float res0, res1;
+
+ // All segments are added together. At t = 0, the integrated curve is always zero.
+
+ // 0 segment is sampled up to the 1 keyframe
+ // First key is always assumed to be at 0 time
+ float t1 = std::min(t, timeValue);
+
+ // 1 segment is sampled from 1 key to 2 key
+ // Last key is always assumed to be at 1 time
+ float t2 = std::max(0.0F, t - timeValue);
+
+ res0 = Polynomial::EvalSegment(t1, segments[0].coeff) * t1 * t1;
+ res1 = Polynomial::EvalSegment(t2, segments[1].coeff) * t2 * t2;
+
+ float finalResult = res0 + res1;
+
+ // Add velocity of previous segments
+ finalResult += velocityValue * std::max(t - timeValue, 0.0F);
+
+ return finalResult;
+ }
+
+ // Evaluate integrated Polynomial curve.
+ // Example: position = EvaluateIntegrated (normalizedTime) * startEnergy
+ // Use Integrate function to for example turn a velocity curve into a position curve.
+ // Expects that t is in the 0...1 range.
+ float EvaluateIntegrated (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+ float res0, res1;
+
+ // All segments are added together. At t = 0, the integrated curve is always zero.
+
+ // 0 segment is sampled up to the 1 keyframe
+ // First key is always assumed to be at 0 time
+ float t1 = std::min(t, timeValue);
+
+ // 1 segment is sampled from 1 key to 2 key
+ // Last key is always assumed to be at 1 time
+ float t2 = std::max(0.0F, t - timeValue);
+
+ res0 = Polynomial::EvalSegment(t1, segments[0].coeff) * t1;
+ res1 = Polynomial::EvalSegment(t2, segments[1].coeff) * t2;
+
+ return (res0 + res1);
+ }
+
+ // Evaluate the curve
+ // extects that t is in the 0...1 range
+ float Evaluate (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+
+ float res0 = Polynomial::EvalSegment(t, segments[0].coeff);
+ float res1 = Polynomial::EvalSegment(t - timeValue, segments[1].coeff);
+
+ float result;
+ if (t > timeValue)
+ result = res1;
+ else
+ result = res0;
+
+ return result;
+ }
+
+ // Find the maximum of a double integrated curve (x: min, y: max)
+ Vector2f FindMinMaxDoubleIntegrated() const;
+
+ // Find the maximum of the integrated curve (x: min, y: max)
+ Vector2f FindMinMaxIntegrated() const;
+
+ // Precalculates polynomials from the animation curve and a scale factor
+ bool BuildOptimizedCurve (const AnimationCurve& editorCurve, float scale);
+
+ // Integrates a velocity curve to be a position curve.
+ // You have to call EvaluateIntegrated to evaluate the curve
+ void Integrate ();
+
+ // Integrates a velocity curve to be a position curve.
+ // You have to call EvaluateDoubleIntegrated to evaluate the curve
+ void DoubleIntegrate ();
+
+ // Add a constant force to a velocity curve
+ // Assumes that you have already called Integrate on the velocity curve.
+ void AddConstantForceToVelocityCurve (float gravity)
+ {
+ for (int i=0;i<kSegmentCount;i++)
+ segments[i].coeff[1] += 0.5F * gravity;
+ }
+};
+
+// Bigger, not so optimized version
+struct PolynomialCurve
+{
+ enum{ kMaxNumSegments = 8 };
+
+ Polynomial segments[kMaxNumSegments]; // Cached polynomial coefficients
+ float integrationCache[kMaxNumSegments]; // Cache of integrated values up until start of segments
+ float doubleIntegrationCache[kMaxNumSegments]; // Cache of double integrated values up until start of segments
+ float times[kMaxNumSegments]; // Time value for end of segment
+
+ int segmentCount;
+
+ // Find the maximum of a double integrated curve (x: min, y: max)
+ Vector2f FindMinMaxDoubleIntegrated() const;
+
+ // Find the maximum of the integrated curve (x: min, y: max)
+ Vector2f FindMinMaxIntegrated() const;
+
+ // Precalculates polynomials from the animation curve and a scale factor
+ bool BuildCurve(const AnimationCurve& editorCurve, float scale);
+
+ // Integrates a velocity curve to be a position curve.
+ // You have to call EvaluateIntegrated to evaluate the curve
+ void Integrate ();
+
+ // Integrates a velocity curve to be a position curve.
+ // You have to call EvaluateDoubleIntegrated to evaluate the curve
+ void DoubleIntegrate ();
+
+ // Evaluates if it is possible to represent animation curve as PolynomialCurve
+ static bool IsValidCurve(const AnimationCurve& editorCurve);
+
+ // Evaluate double integrated Polynomial curve.
+ // Example: position = EvaluateDoubleIntegrated (normalizedTime) * startEnergy^2
+ // Use DoubleIntegrate function to for example turn a force curve into a position curve.
+ // Expects that t is in the 0...1 range.
+ float EvaluateDoubleIntegrated (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+
+ float prevTimeValue = 0.0f;
+ for(int i = 0; i < segmentCount; i++)
+ {
+ if(t <= times[i])
+ {
+ const float time = t - prevTimeValue;
+ return doubleIntegrationCache[i] + integrationCache[i] * time + Polynomial::EvalSegment(time, segments[i].coeff) * time * time;
+ }
+ prevTimeValue = times[i];
+ }
+ DebugAssert(!"PolyCurve: Outside segment range!");
+ return 1.0f;
+ }
+
+ // Evaluate integrated Polynomial curve.
+ // Example: position = EvaluateIntegrated (normalizedTime) * startEnergy
+ // Use Integrate function to for example turn a velocity curve into a position curve.
+ // Expects that t is in the 0...1 range.
+ float EvaluateIntegrated (float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+
+ float prevTimeValue = 0.0f;
+ for(int i = 0; i < segmentCount; i++)
+ {
+ if(t <= times[i])
+ {
+ const float time = t - prevTimeValue;
+ return integrationCache[i] + Polynomial::EvalSegment(time, segments[i].coeff) * time;
+ }
+ prevTimeValue = times[i];
+ }
+ DebugAssert(!"PolyCurve: Outside segment range!");
+ return 1.0f;
+ }
+
+ // Evaluate the curve
+ // extects that t is in the 0...1 range
+ float Evaluate(float t) const
+ {
+ DebugAssert(t >= -0.01F && t <= 1.01F);
+
+ float prevTimeValue = 0.0f;
+ for(int i = 0; i < segmentCount; i++)
+ {
+ if(t <= times[i])
+ return Polynomial::EvalSegment(t - prevTimeValue, segments[i].coeff);
+ prevTimeValue = times[i];
+ }
+ DebugAssert(!"PolyCurve: Outside segment range!");
+ return 1.0f;
+ }
+};
+
+
+void SetPolynomialCurveToValue (AnimationCurve& a, OptimizedPolynomialCurve& c, float value);
+void SetPolynomialCurveToLinear (AnimationCurve& a, OptimizedPolynomialCurve& c);
+void ConstrainToPolynomialCurve (AnimationCurve& curve);
+bool IsValidPolynomialCurve (const AnimationCurve& curve);
+void CalculateMinMax(Vector2f& minmax, float value);
+
+#endif // POLYONOMIAL_CURVE_H