skia2/modules/particles/include/SkCurve.h
Brian Osman fe49163cd1 Major rewrite of the particle system based on the SkSL interpreter
This removes all of the fixed-function particle affector classes.
Instead, each particle effect just has two SkSL snippets, one for
spawn logic, and one for update logic. Each one gets an inout copy
of the particle struct. Ultimately, this makes the effects much
simpler and smaller, while also being far more flexible (you can
do whatever you want with any values you want). Finally, because
the interpreter is vectorized and a particular effect's scripts
are usually tuned to the specific behaviors desired, it's faster
on basically every effect I compared.

I re-created all of the old effects in the new system. Many just
use pure SkSL (no curves or anything). Some of the old curve and
path/text stuff was very handy, though - so those are now exposed
as external values in the interpreter. Basically, an effect can
have any number of named "bindings" that are a callable thing.
This can be a path, text (shortcut for making fancy paths), curve,
or color curve. The path ones return a float4 with position and
normal, the curves return one or four floats.

... and this transposes all of the particle data storage into
SoA form, so that it can use the much faster interpreter entry
point.

Change-Id: Iebe711c45994c4201041b12d171af976bc5e758e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/222057
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Reed <reed@google.com>
2019-07-25 19:59:03 +00:00

125 lines
4.0 KiB
C++

/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkCurve_DEFINED
#define SkCurve_DEFINED
#include "include/core/SkColor.h"
#include "include/private/SkTArray.h"
class SkFieldVisitor;
class SkRandom;
/**
* SkCurve implements a keyframed 1D function, useful for animating values over time. This pattern
* is common in digital content creation tools. An SkCurve might represent rotation, scale, opacity,
* or any other scalar quantity.
*
* An SkCurve has a logical domain of [0, 1], and is made of one or more SkCurveSegments.
* Each segment describes the behavior of the curve in some sub-domain. For an SkCurve with N
* segments, there are (N - 1) intermediate x-values that subdivide the domain. The first and last
* x-values are implicitly 0 and 1:
*
* 0 ... x[0] ... x[1] ... ... 1
* Segment_0 Segment_1 ... Segment_N-1
*
* Each segment describes a function over [0, 1] - x-values are re-normalized to the segment's
* domain when being evaluated. The segments are cubic polynomials, defined by four values (fMin).
* These are the values at x=0 and x=1, as well as control points at x=1/3 and x=2/3.
*
* For segments with fConstant == true, only the first value is used (fMin[0]).
*
* Each segment has two additional features for creating interesting (and varied) animation:
* - A segment can be ranged. Ranged segments have two sets of coefficients, and a random value
* taken from the particle's SkRandom is used to lerp betwen them. Typically, the SkRandom is
* in the same state at each call, so this value is stable. That causes a ranged SkCurve to
* produce a single smooth cubic function somewhere within the range defined by fMin and fMax.
* - A segment can be bidirectional. In that case, after a value is computed, it will be negated
* 50% of the time.
*/
enum SkCurveSegmentType {
kConstant_SegmentType,
kLinear_SegmentType,
kCubic_SegmentType,
};
struct SkCurveSegment {
float eval(float x, float t, bool negate) const;
void visitFields(SkFieldVisitor* v);
void setConstant(float c) {
fType = kConstant_SegmentType;
fRanged = false;
fMin[0] = c;
}
float fMin[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
float fMax[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
int fType = kConstant_SegmentType;
bool fRanged = false;
bool fBidirectional = false;
};
struct SkCurve {
SkCurve(float c = 0.0f) {
fSegments.push_back().setConstant(c);
}
float eval(float x, SkRandom& random) const;
void visitFields(SkFieldVisitor* v);
// It should always be true that (fXValues.count() + 1) == fSegments.count()
SkTArray<float, true> fXValues;
SkTArray<SkCurveSegment, true> fSegments;
};
/**
* SkColorCurve is similar to SkCurve, but keyframes 4D values - specifically colors. Because
* negative colors rarely make sense, SkColorCurves do not support bidirectional segments, but
* support all other features (including cubic interpolation).
*/
struct SkColorCurveSegment {
SkColorCurveSegment() {
for (int i = 0; i < 4; ++i) {
fMin[i] = { 1.0f, 1.0f, 1.0f, 1.0f };
fMax[i] = { 1.0f, 1.0f, 1.0f, 1.0f };
}
}
SkColor4f eval(float x, float t) const;
void visitFields(SkFieldVisitor* v);
void setConstant(SkColor4f c) {
fType = kConstant_SegmentType;
fRanged = false;
fMin[0] = c;
}
SkColor4f fMin[4];
SkColor4f fMax[4];
int fType = kConstant_SegmentType;
bool fRanged = false;
};
struct SkColorCurve {
SkColorCurve(SkColor4f c = { 1.0f, 1.0f, 1.0f, 1.0f }) {
fSegments.push_back().setConstant(c);
}
SkColor4f eval(float x, SkRandom& random) const;
void visitFields(SkFieldVisitor* v);
SkTArray<float, true> fXValues;
SkTArray<SkColorCurveSegment, true> fSegments;
};
#endif // SkCurve_DEFINED