diff --git a/modules/canvaskit/canvaskit/extra.html b/modules/canvaskit/canvaskit/extra.html index 91c8738ec3..d41ff72e90 100644 --- a/modules/canvaskit/canvaskit/extra.html +++ b/modules/canvaskit/canvaskit/extra.html @@ -1,4 +1,4 @@ - + CanvasKit Extra features (Skia via Web Assembly) @@ -183,8 +183,9 @@ } const context = CanvasKit.currentContext(); const canvas = surface.getCanvas(); + canvas.translate(250, 450); - const particles = CanvasKit.MakeParticles(JSON.stringify(snowfall)); + const particles = CanvasKit.MakeParticles(JSON.stringify(curves)); particles.start(Date.now() / 1000.0, true); function drawFrame(canvas) { @@ -197,58 +198,35 @@ surface.requestAnimationFrame(drawFrame); } -const snowfall = { - "MaxCount": 4096, +const curves = { + "MaxCount": 1000, "Drawable": { "Type": "SkCircleDrawable", - "Radius": 1 + "Radius": 2 }, "EffectCode": [ "void effectSpawn(inout Effect effect) {", - " effect.rate = 30;", - "}", - "", - "void effectUpdate(inout Effect effect) {", + " effect.rate = 200;", + " effect.color = float4(1, 0, 0, 1);", "}", "" ], "Code": [ "void spawn(inout Particle p) {", - " p.lifetime = 10;", - " p.vel.y = 10 + rand * 20;", - " p.vel.x = -5 + 10 * rand;", - " p.pos.x = rand * 500;", + " p.lifetime = 3 + rand;", + " p.vel.y = -50;", "}", "", "void update(inout Particle p) {", - " p.scale = size(p.age);", + " float w = mix(15, 3, p.age);", + " p.pos.x = sin(radians(p.age * 320)) * mix(25, 10, p.age) + mix(-w, w, rand);", + " if (rand < 0.5) { p.pos.x = -p.pos.x; }", + "", + " p.color.g = (mix(75, 220, p.age) + mix(-30, 30, rand)) / 255;", "}", "" ], - "Bindings": [ - { - "Type": "SkCurveBinding", - "Name": "size", - "Curve": { - "XValues": [], - "Segments": [ - { - "Type": "Cubic", - "Ranged": true, - "Bidirectional": false, - "A0": 10, - "B0": 10, - "C0": 10, - "D0": 0, - "A1": 10, - "B1": 0, - "C1": 0, - "D1": 0 - } - ] - } - } - ] + "Bindings": [] }; function SurfaceAPI1(CanvasKit) { diff --git a/modules/particles/include/SkCurve.h b/modules/particles/include/SkCurve.h deleted file mode 100644 index c308aa563e..0000000000 --- a/modules/particles/include/SkCurve.h +++ /dev/null @@ -1,124 +0,0 @@ -/* -* 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 fXValues; - SkTArray 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 fXValues; - SkTArray fSegments; -}; - -#endif // SkCurve_DEFINED diff --git a/modules/particles/include/SkParticleBinding.h b/modules/particles/include/SkParticleBinding.h index 184bb07f8d..39665f6af0 100644 --- a/modules/particles/include/SkParticleBinding.h +++ b/modules/particles/include/SkParticleBinding.h @@ -14,8 +14,6 @@ #include -struct SkCurve; -struct SkColorCurve; class SkParticleEffect; class SkParticleEffectParams; class SkRandom; @@ -60,14 +58,6 @@ public: * each kind of binding is described below. */ - // Binds an SkCurve to an effect's SkSL. The curve is a one-dimensional function, described - // in SkCurve.h. It is called in the SkSL as 'name(t)', and returns a single float value. - static sk_sp MakeCurve(const char* name, const SkCurve& curve); - - // Binds an SkColorCurve to an effect's SkSL. The curve is a one-dimensional, function, - // described in SkCurve.h. It is called in the SkSL as 'name(t)', and returns a float4 value. - static sk_sp MakeColorCurve(const char* name, const SkColorCurve& curve); - // Binds an SkPath to an effect's SkSL. The path is specified using SVG syntax. It is called // in the SkSL as 'name(t)'. 't' is a normalized distance along the path. This returns a float4 // value, containing the position in .xy, and the normal in .zw. diff --git a/modules/particles/include/SkReflected.h b/modules/particles/include/SkReflected.h index 9ad3520161..beb7291844 100644 --- a/modules/particles/include/SkReflected.h +++ b/modules/particles/include/SkReflected.h @@ -15,7 +15,6 @@ #include // std::function #include -struct SkCurve; class SkFieldVisitor; struct SkPoint; class SkString; @@ -163,9 +162,6 @@ public: }; virtual void visit(const char*, int&, const EnumStringMapping*, int count) = 0; - // Specific virtual signature for SkCurve, to allow for heavily customized UI in SkGuiVisitor. - virtual void visit(const char* name, SkCurve& c); - // Default visit function for structs with no special behavior. It is assumed that any such // struct implements visitFields(SkFieldVisitor*) to recursively visit each of its fields. template diff --git a/modules/particles/particles.gni b/modules/particles/particles.gni index 49a313ab57..bd0bce1f50 100644 --- a/modules/particles/particles.gni +++ b/modules/particles/particles.gni @@ -7,7 +7,6 @@ _src = get_path_info("src", "abspath") skia_particle_sources = [ - "$_src/SkCurve.cpp", "$_src/SkParticleBinding.cpp", "$_src/SkParticleDrawable.cpp", "$_src/SkParticleEffect.cpp", diff --git a/modules/particles/src/SkCurve.cpp b/modules/particles/src/SkCurve.cpp deleted file mode 100644 index f54ab31166..0000000000 --- a/modules/particles/src/SkCurve.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* -* Copyright 2019 Google LLC -* -* Use of this source code is governed by a BSD-style license that can be -* found in the LICENSE file. -*/ - -#include "include/utils/SkRandom.h" -#include "modules/particles/include/SkCurve.h" -#include "modules/particles/include/SkReflected.h" - -constexpr SkFieldVisitor::EnumStringMapping gCurveSegmentTypeMapping[] = { - { kConstant_SegmentType, "Constant" }, - { kLinear_SegmentType, "Linear" }, - { kCubic_SegmentType, "Cubic" }, -}; - -static SkColor4f operator+(SkColor4f c1, SkColor4f c2) { - return { c1.fR + c2.fR, c1.fG + c2.fG, c1.fB + c2.fB, c1.fA + c2.fA }; -} - -static SkColor4f operator-(SkColor4f c1, SkColor4f c2) { - return { c1.fR - c2.fR, c1.fG - c2.fG, c1.fB - c2.fB, c1.fA - c2.fA }; -} - -template -static T eval_cubic(const T* pts, float x) { - float ix = (1 - x); - return pts[0]*(ix*ix*ix) + pts[1]*(3*ix*ix*x) + pts[2]*(3*ix*x*x) + pts[3]*(x*x*x); -} - -template -static T eval_segment(const T* pts, float x, int type) { - switch (type) { - case kLinear_SegmentType: - return pts[0] + (pts[3] - pts[0]) * x; - case kCubic_SegmentType: - return eval_cubic(pts, x); - case kConstant_SegmentType: - default: - return pts[0]; - } -} - -float SkCurveSegment::eval(float x, float t, bool negate) const { - float result = eval_segment(fMin, x, fType); - if (fRanged) { - result += (eval_segment(fMax, x, fType) - result) * t; - } - if (fBidirectional && negate) { - result = -result; - } - return result; -} - -void SkCurveSegment::visitFields(SkFieldVisitor* v) { - v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping)); - v->visit("Ranged", fRanged); - v->visit("Bidirectional", fBidirectional); - v->visit("A0", fMin[0]); - if (fType == kCubic_SegmentType) { - v->visit("B0", fMin[1]); - v->visit("C0", fMin[2]); - } - if (fType != kConstant_SegmentType) { - v->visit("D0", fMin[3]); - } - if (fRanged) { - v->visit("A1", fMax[0]); - if (fType == kCubic_SegmentType) { - v->visit("B1", fMax[1]); - v->visit("C1", fMax[2]); - } - if (fType != kConstant_SegmentType) { - v->visit("D1", fMax[3]); - } - } -} - -float SkCurve::eval(float x, SkRandom& random) const { - SkASSERT(fSegments.count() == fXValues.count() + 1); - - int i = 0; - for (; i < fXValues.count(); ++i) { - if (x <= fXValues[i]) { - break; - } - } - - float rangeMin = (i == 0) ? 0.0f : fXValues[i - 1]; - float rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i]; - float segmentX = (x - rangeMin) / (rangeMax - rangeMin); - if (!sk_float_isfinite(segmentX)) { - segmentX = rangeMin; - } - SkASSERT(0.0f <= segmentX && segmentX <= 1.0f); - - // Always pull t and negate here, so that the stable generator behaves consistently, even if - // our segments use an inconsistent feature-set. - float t = random.nextF(); - bool negate = random.nextBool(); - return fSegments[i].eval(segmentX, t, negate); -} - -void SkCurve::visitFields(SkFieldVisitor* v) { - v->visit("XValues", fXValues); - v->visit("Segments", fSegments); - - // Validate and fixup - if (fSegments.empty()) { - fSegments.push_back().setConstant(0.0f); - } - fXValues.resize_back(fSegments.count() - 1); - for (int i = 0; i < fXValues.count(); ++i) { - fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f); - } -} - -SkColor4f SkColorCurveSegment::eval(float x, float t) const { - SkColor4f result = eval_segment(fMin, x, fType); - if (fRanged) { - result = result + (eval_segment(fMax, x, fType) - result) * t; - } - return result; -} - -void SkColorCurveSegment::visitFields(SkFieldVisitor* v) { - v->visit("Type", fType, gCurveSegmentTypeMapping, SK_ARRAY_COUNT(gCurveSegmentTypeMapping)); - v->visit("Ranged", fRanged); - v->visit("A0", fMin[0]); - if (fType == kCubic_SegmentType) { - v->visit("B0", fMin[1]); - v->visit("C0", fMin[2]); - } - if (fType != kConstant_SegmentType) { - v->visit("D0", fMin[3]); - } - if (fRanged) { - v->visit("A1", fMax[0]); - if (fType == kCubic_SegmentType) { - v->visit("B1", fMax[1]); - v->visit("C1", fMax[2]); - } - if (fType != kConstant_SegmentType) { - v->visit("D1", fMax[3]); - } - } -} - -SkColor4f SkColorCurve::eval(float x, SkRandom& random) const { - SkASSERT(fSegments.count() == fXValues.count() + 1); - - int i = 0; - for (; i < fXValues.count(); ++i) { - if (x <= fXValues[i]) { - break; - } - } - - float rangeMin = (i == 0) ? 0.0f : fXValues[i - 1]; - float rangeMax = (i == fXValues.count()) ? 1.0f : fXValues[i]; - float segmentX = (x - rangeMin) / (rangeMax - rangeMin); - if (!sk_float_isfinite(segmentX)) { - segmentX = rangeMin; - } - SkASSERT(0.0f <= segmentX && segmentX <= 1.0f); - return fSegments[i].eval(segmentX, random.nextF()); -} - -void SkColorCurve::visitFields(SkFieldVisitor* v) { - v->visit("XValues", fXValues); - v->visit("Segments", fSegments); - - // Validate and fixup - if (fSegments.empty()) { - fSegments.push_back().setConstant(SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f }); - } - fXValues.resize_back(fSegments.count() - 1); - for (int i = 0; i < fXValues.count(); ++i) { - fXValues[i] = SkTPin(fXValues[i], i > 0 ? fXValues[i - 1] : 0.0f, 1.0f); - } -} diff --git a/modules/particles/src/SkParticleBinding.cpp b/modules/particles/src/SkParticleBinding.cpp index 12b48b3c03..e1a45324a1 100644 --- a/modules/particles/src/SkParticleBinding.cpp +++ b/modules/particles/src/SkParticleBinding.cpp @@ -12,7 +12,6 @@ #include "include/utils/SkParsePath.h" #include "include/utils/SkRandom.h" #include "include/utils/SkTextUtils.h" -#include "modules/particles/include/SkCurve.h" #include "modules/particles/include/SkParticleEffect.h" #include "modules/particles/include/SkReflected.h" #include "src/sksl/SkSLCompiler.h" @@ -21,96 +20,6 @@ void SkParticleBinding::visitFields(SkFieldVisitor* v) { v->visit("Name", fName); } -// Exposes an SkCurve as an external, callable value. c(x) returns a float. -class SkCurveExternalValue : public SkParticleExternalValue { -public: - SkCurveExternalValue(const char* name, SkSL::Compiler& compiler, const SkCurve& curve) - : SkParticleExternalValue(name, compiler, *compiler.context().fFloat_Type) - , fCurve(curve) { } - - bool canCall() const override { return true; } - int callParameterCount() const override { return 1; } - void getCallParameterTypes(const SkSL::Type** outTypes) const override { - outTypes[0] = fCompiler.context().fFloat_Type.get(); - } - - void call(int index, float* arguments, float* outReturn) override { - *outReturn = fCurve.eval(*arguments, fRandom[index]); - } - -private: - SkCurve fCurve; -}; - -class SkCurveBinding : public SkParticleBinding { -public: - SkCurveBinding(const char* name = "", const SkCurve& curve = 0.0f) - : SkParticleBinding(name) - , fCurve(curve) {} - - REFLECTED(SkCurveBinding, SkParticleBinding) - - void visitFields(SkFieldVisitor* v) override { - SkParticleBinding::visitFields(v); - v->visit("Curve", fCurve); - } - - std::unique_ptr toValue(SkSL::Compiler& compiler) override { - return std::unique_ptr( - new SkCurveExternalValue(fName.c_str(), compiler, fCurve)); - } - -private: - SkCurve fCurve; -}; - -// Exposes an SkColorCurve as an external, callable value. c(x) returns a float4. -class SkColorCurveExternalValue : public SkParticleExternalValue { -public: - SkColorCurveExternalValue(const char* name, SkSL::Compiler& compiler, const SkColorCurve& curve) - : SkParticleExternalValue(name, compiler, *compiler.context().fFloat4_Type) - , fCurve(curve) { - } - - bool canCall() const override { return true; } - int callParameterCount() const override { return 1; } - void getCallParameterTypes(const SkSL::Type** outTypes) const override { - outTypes[0] = fCompiler.context().fFloat_Type.get(); - } - - void call(int index, float* arguments, float* outReturn) override { - SkColor4f color = fCurve.eval(*arguments, fRandom[index]); - memcpy(outReturn, color.vec(), 4 * sizeof(float)); - } - -private: - SkColorCurve fCurve; -}; - -class SkColorCurveBinding : public SkParticleBinding { -public: - SkColorCurveBinding(const char* name = "", - const SkColorCurve& curve = SkColor4f{ 1.0f, 1.0f, 1.0f, 1.0f }) - : SkParticleBinding(name) - , fCurve(curve) { - } - - REFLECTED(SkColorCurveBinding, SkParticleBinding) - - void visitFields(SkFieldVisitor* v) override { - SkParticleBinding::visitFields(v); - v->visit("Curve", fCurve); - } - - std::unique_ptr toValue(SkSL::Compiler& compiler) override { - return std::unique_ptr( - new SkColorCurveExternalValue(fName.c_str(), compiler, fCurve)); - } - -private: - SkColorCurve fCurve; -}; - class SkEffectExternalValue : public SkParticleExternalValue { public: SkEffectExternalValue(const char* name, SkSL::Compiler& compiler, @@ -295,15 +204,6 @@ private: SkPathContours fContours; }; -sk_sp SkParticleBinding::MakeCurve(const char* name, const SkCurve& curve) { - return sk_sp(new SkCurveBinding(name, curve)); -} - -sk_sp SkParticleBinding::MakeColorCurve(const char* name, - const SkColorCurve& curve) { - return sk_sp(new SkColorCurveBinding(name, curve)); -} - sk_sp SkParticleBinding::MakeEffectBinding( const char* name, sk_sp params) { return sk_sp(new SkEffectBinding(name, params)); @@ -315,8 +215,6 @@ sk_sp SkParticleBinding::MakePathBinding(const char* name, co void SkParticleBinding::RegisterBindingTypes() { REGISTER_REFLECTED(SkParticleBinding); - REGISTER_REFLECTED(SkCurveBinding); - REGISTER_REFLECTED(SkColorCurveBinding); REGISTER_REFLECTED(SkEffectBinding); REGISTER_REFLECTED(SkPathBinding); REGISTER_REFLECTED(SkTextBinding); diff --git a/modules/particles/src/SkParticleEffect.cpp b/modules/particles/src/SkParticleEffect.cpp index 3a9e5fbe16..db6724b2e3 100644 --- a/modules/particles/src/SkParticleEffect.cpp +++ b/modules/particles/src/SkParticleEffect.cpp @@ -8,7 +8,6 @@ #include "modules/particles/include/SkParticleEffect.h" #include "include/core/SkPaint.h" -#include "modules/particles/include/SkCurve.h" #include "modules/particles/include/SkParticleBinding.h" #include "modules/particles/include/SkParticleDrawable.h" #include "modules/particles/include/SkReflected.h" diff --git a/modules/particles/src/SkReflected.cpp b/modules/particles/src/SkReflected.cpp index 94e59cac67..247f7a7234 100644 --- a/modules/particles/src/SkReflected.cpp +++ b/modules/particles/src/SkReflected.cpp @@ -7,8 +7,6 @@ #include "modules/particles/include/SkReflected.h" -#include "modules/particles/include/SkCurve.h" - SkSTArray<16, const SkReflected::Type*, true> SkReflected::gTypes; void SkReflected::VisitTypes(std::function visitor) { @@ -16,9 +14,3 @@ void SkReflected::VisitTypes(std::function visitor) { visitor(type); } } - -void SkFieldVisitor::visit(const char* name, SkCurve& c) { - this->enterObject(name); - c.visitFields(this); - this->exitObject(); -} diff --git a/resources/particles/curves.json b/resources/particles/curves.json index 864e7ed7ab..a3db6a5279 100644 --- a/resources/particles/curves.json +++ b/resources/particles/curves.json @@ -7,6 +7,7 @@ "EffectCode": [ "void effectSpawn(inout Effect effect) {", " effect.rate = 200;", + " effect.color = float4(1, 0, 0, 1);", "}", "" ], @@ -17,50 +18,13 @@ "}", "", "void update(inout Particle p) {", - " p.pos.x = wave(p.age);", - " p.color = color(p.age);", + " float w = mix(15, 3, p.age);", + " p.pos.x = sin(radians(p.age * 320)) * mix(25, 10, p.age) + mix(-w, w, rand);", + " if (rand < 0.5) { p.pos.x = -p.pos.x; }", + "", + " p.color.g = (mix(75, 220, p.age) + mix(-30, 30, rand)) / 255;", "}", "" ], - "Bindings": [ - { - "Type": "SkCurveBinding", - "Name": "wave", - "Curve": { - "XValues": [], - "Segments": [ - { - "Type": "Cubic", - "Ranged": true, - "Bidirectional": true, - "A0": -20, - "B0": 50, - "C0": -30, - "D0": -10, - "A1": 20, - "B1": 60, - "C1": -20, - "D1": 0 - } - ] - } - }, - { - "Type": "SkColorCurveBinding", - "Name": "color", - "Curve": { - "XValues": [], - "Segments": [ - { - "Type": "Linear", - "Ranged": true, - "A0": [ 1, 0, 0, 1 ], - "D0": [ 1, 0.735294, 0, 1 ], - "A1": [ 1, 0.588235, 0, 1 ], - "D1": [ 0.941177, 1, 0, 1 ] - } - ] - } - } - ] + "Bindings": [] } \ No newline at end of file diff --git a/site/user/modules/particles.md b/site/user/modules/particles.md index ad3046396d..8dfbcad356 100644 --- a/site/user/modules/particles.md +++ b/site/user/modules/particles.md @@ -38,7 +38,7 @@ Samples
- Curves
@@ -87,7 +87,7 @@ Samples }).ready().then((CK) => { CanvasKit = CK; ParticleExample(CanvasKit, 'confetti', confetti, 200, 200); - ParticleExample(CanvasKit, 'curves', curves, 200, 200); + ParticleExample(CanvasKit, 'curves', curves, 200, 300); ParticleExample(CanvasKit, 'fireworks', fireworks, 200, 300); ParticleExample(CanvasKit, 'raincloud', raincloud, 200, 100); ParticleExample(CanvasKit, 'text', text, 75, 250); @@ -178,7 +178,8 @@ const curves = { }, "EffectCode": [ "void effectSpawn(inout Effect effect) {", - " effect.rate = 200; effect.pos.y = 100;", + " effect.rate = 200;", + " effect.color = float4(1, 0, 0, 1);", "}", "" ], @@ -189,52 +190,15 @@ const curves = { "}", "", "void update(inout Particle p) {", - " p.pos.x = wave(p.age);", - " p.color = color(p.age);", + " float w = mix(15, 3, p.age);", + " p.pos.x = sin(radians(p.age * 320)) * mix(25, 10, p.age) + mix(-w, w, rand);", + " if (rand < 0.5) { p.pos.x = -p.pos.x; }", + "", + " p.color.g = (mix(75, 220, p.age) + mix(-30, 30, rand)) / 255;", "}", "" ], - "Bindings": [ - { - "Type": "SkCurveBinding", - "Name": "wave", - "Curve": { - "XValues": [], - "Segments": [ - { - "Type": "Cubic", - "Ranged": true, - "Bidirectional": true, - "A0": -20, - "B0": 50, - "C0": -30, - "D0": -10, - "A1": 20, - "B1": 60, - "C1": -20, - "D1": 0 - } - ] - } - }, - { - "Type": "SkColorCurveBinding", - "Name": "color", - "Curve": { - "XValues": [], - "Segments": [ - { - "Type": "Linear", - "Ranged": true, - "A0": [ 1, 0, 0, 1 ], - "D0": [ 1, 0.735294, 0, 0.2 ], - "A1": [ 1, 0.588235, 0, 1 ], - "D1": [ 0.941177, 1, 0, 0.2 ] - } - ] - } - } - ] + "Bindings": [] }; const fireworks = {