1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2021 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlendMode.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkM44.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPicture.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPictureRecorder.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSamplingOptions.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkShader.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTileMode.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkRuntimeEffect.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTPin.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/src/Adapter.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/src/SkottiePriv.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/src/SkottieValue.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "modules/skottie/src/effects/Effects.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "modules/sksg/include/SkSGNode.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "modules/sksg/include/SkSGRenderNode.h"
34*c8dee2aaSAndroid Build Coastguard Worker
35*c8dee2aaSAndroid Build Coastguard Worker #include <array>
36*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
37*c8dee2aaSAndroid Build Coastguard Worker #include <cstdio>
38*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
39*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker namespace skjson {
42*c8dee2aaSAndroid Build Coastguard Worker class ArrayValue;
43*c8dee2aaSAndroid Build Coastguard Worker }
44*c8dee2aaSAndroid Build Coastguard Worker namespace sksg {
45*c8dee2aaSAndroid Build Coastguard Worker class InvalidationController;
46*c8dee2aaSAndroid Build Coastguard Worker }
47*c8dee2aaSAndroid Build Coastguard Worker
48*c8dee2aaSAndroid Build Coastguard Worker namespace skottie::internal {
49*c8dee2aaSAndroid Build Coastguard Worker namespace {
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker // This shader maps its child shader onto a sphere. To simplify things, we set it up such that:
52*c8dee2aaSAndroid Build Coastguard Worker //
53*c8dee2aaSAndroid Build Coastguard Worker // - the sphere is centered at origin and has r == 1
54*c8dee2aaSAndroid Build Coastguard Worker // - the eye is positioned at (0,0,eye_z), where eye_z is chosen to visually match AE
55*c8dee2aaSAndroid Build Coastguard Worker // - the POI for a given pixel is on the z = 0 plane (x,y,0)
56*c8dee2aaSAndroid Build Coastguard Worker // - we're only rendering inside the projected circle, which guarantees a quadratic solution
57*c8dee2aaSAndroid Build Coastguard Worker //
58*c8dee2aaSAndroid Build Coastguard Worker // Effect stages:
59*c8dee2aaSAndroid Build Coastguard Worker //
60*c8dee2aaSAndroid Build Coastguard Worker // 1) ray-cast to find the sphere intersection (selectable front/back solution);
61*c8dee2aaSAndroid Build Coastguard Worker // given the sphere geometry, this is also the normal
62*c8dee2aaSAndroid Build Coastguard Worker // 2) rotate the normal
63*c8dee2aaSAndroid Build Coastguard Worker // 3) UV-map the sphere
64*c8dee2aaSAndroid Build Coastguard Worker // 4) scale uv to source size and sample
65*c8dee2aaSAndroid Build Coastguard Worker // 5) apply lighting model
66*c8dee2aaSAndroid Build Coastguard Worker //
67*c8dee2aaSAndroid Build Coastguard Worker // Note: the current implementation uses two passes for two-side ("full") rendering, on the
68*c8dee2aaSAndroid Build Coastguard Worker // assumption that in practice most textures are opaque and two-side mode is infrequent;
69*c8dee2aaSAndroid Build Coastguard Worker // if this proves to be problematic, we could expand the implementation to blend both sides
70*c8dee2aaSAndroid Build Coastguard Worker // in one pass.
71*c8dee2aaSAndroid Build Coastguard Worker //
72*c8dee2aaSAndroid Build Coastguard Worker static constexpr char gSphereSkSL[] =
73*c8dee2aaSAndroid Build Coastguard Worker "uniform shader child;"
74*c8dee2aaSAndroid Build Coastguard Worker
75*c8dee2aaSAndroid Build Coastguard Worker "uniform half3x3 rot_matrix;"
76*c8dee2aaSAndroid Build Coastguard Worker "uniform half2 child_scale;"
77*c8dee2aaSAndroid Build Coastguard Worker "uniform half side_select;"
78*c8dee2aaSAndroid Build Coastguard Worker
79*c8dee2aaSAndroid Build Coastguard Worker // apply_light()
80*c8dee2aaSAndroid Build Coastguard Worker "%s"
81*c8dee2aaSAndroid Build Coastguard Worker
82*c8dee2aaSAndroid Build Coastguard Worker "half3 to_sphere(half3 EYE) {"
83*c8dee2aaSAndroid Build Coastguard Worker "half eye_z2 = EYE.z*EYE.z;"
84*c8dee2aaSAndroid Build Coastguard Worker
85*c8dee2aaSAndroid Build Coastguard Worker "half a = dot(EYE, EYE),"
86*c8dee2aaSAndroid Build Coastguard Worker "b = -2*eye_z2,"
87*c8dee2aaSAndroid Build Coastguard Worker "c = eye_z2 - 1,"
88*c8dee2aaSAndroid Build Coastguard Worker "t = (-b + side_select*sqrt(b*b - 4*a*c))/(2*a);"
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker "return half3(0, 0, -EYE.z) + EYE*t;"
91*c8dee2aaSAndroid Build Coastguard Worker "}"
92*c8dee2aaSAndroid Build Coastguard Worker
93*c8dee2aaSAndroid Build Coastguard Worker "half4 main(float2 xy) {"
94*c8dee2aaSAndroid Build Coastguard Worker "half3 EYE = half3(xy, -5.5),"
95*c8dee2aaSAndroid Build Coastguard Worker "N = to_sphere(EYE),"
96*c8dee2aaSAndroid Build Coastguard Worker "RN = rot_matrix*N;"
97*c8dee2aaSAndroid Build Coastguard Worker
98*c8dee2aaSAndroid Build Coastguard Worker "half kRPI = 1/3.1415927;"
99*c8dee2aaSAndroid Build Coastguard Worker
100*c8dee2aaSAndroid Build Coastguard Worker "half2 UV = half2("
101*c8dee2aaSAndroid Build Coastguard Worker "0.5 + kRPI * 0.5 * atan(RN.x, RN.z),"
102*c8dee2aaSAndroid Build Coastguard Worker "0.5 + kRPI * asin(RN.y)"
103*c8dee2aaSAndroid Build Coastguard Worker ");"
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker "return apply_light(EYE, N, child.eval(UV*child_scale));"
106*c8dee2aaSAndroid Build Coastguard Worker "}"
107*c8dee2aaSAndroid Build Coastguard Worker ;
108*c8dee2aaSAndroid Build Coastguard Worker
109*c8dee2aaSAndroid Build Coastguard Worker // CC Sphere uses a Phong-like lighting model:
110*c8dee2aaSAndroid Build Coastguard Worker //
111*c8dee2aaSAndroid Build Coastguard Worker // - "ambient" controls the intensity of the texture color
112*c8dee2aaSAndroid Build Coastguard Worker // - "diffuse" controls a multiplicative mix of texture and light color
113*c8dee2aaSAndroid Build Coastguard Worker // - "specular" controls a light color specular component
114*c8dee2aaSAndroid Build Coastguard Worker // - "roughness" is the specular exponent reciprocal
115*c8dee2aaSAndroid Build Coastguard Worker // - "light intensity" modulates the diffuse and specular components (but not ambient)
116*c8dee2aaSAndroid Build Coastguard Worker // - "light height" and "light direction" specify the light source position in spherical coords
117*c8dee2aaSAndroid Build Coastguard Worker //
118*c8dee2aaSAndroid Build Coastguard Worker // Implementation-wise, light intensity/height/direction are all combined into l_vec.
119*c8dee2aaSAndroid Build Coastguard Worker // For efficiency, we fall back to a stripped-down shader (ambient-only) when the diffuse & specular
120*c8dee2aaSAndroid Build Coastguard Worker // components are not used.
121*c8dee2aaSAndroid Build Coastguard Worker //
122*c8dee2aaSAndroid Build Coastguard Worker // TODO: "metal" and "reflective" parameters are ignored.
123*c8dee2aaSAndroid Build Coastguard Worker static constexpr char gBasicLightSkSL[] =
124*c8dee2aaSAndroid Build Coastguard Worker "uniform half l_coeff_ambient;"
125*c8dee2aaSAndroid Build Coastguard Worker
126*c8dee2aaSAndroid Build Coastguard Worker "half4 apply_light(half3 EYE, half3 N, half4 c) {"
127*c8dee2aaSAndroid Build Coastguard Worker "c.rgb *= l_coeff_ambient;"
128*c8dee2aaSAndroid Build Coastguard Worker "return c;"
129*c8dee2aaSAndroid Build Coastguard Worker "}"
130*c8dee2aaSAndroid Build Coastguard Worker ;
131*c8dee2aaSAndroid Build Coastguard Worker
132*c8dee2aaSAndroid Build Coastguard Worker static constexpr char gFancyLightSkSL[] =
133*c8dee2aaSAndroid Build Coastguard Worker "uniform half3 l_vec;"
134*c8dee2aaSAndroid Build Coastguard Worker "uniform half3 l_color;"
135*c8dee2aaSAndroid Build Coastguard Worker "uniform half l_coeff_ambient;"
136*c8dee2aaSAndroid Build Coastguard Worker "uniform half l_coeff_diffuse;"
137*c8dee2aaSAndroid Build Coastguard Worker "uniform half l_coeff_specular;"
138*c8dee2aaSAndroid Build Coastguard Worker "uniform half l_specular_exp;"
139*c8dee2aaSAndroid Build Coastguard Worker
140*c8dee2aaSAndroid Build Coastguard Worker "half4 apply_light(half3 EYE, half3 N, half4 c) {"
141*c8dee2aaSAndroid Build Coastguard Worker "half3 LR = reflect(-l_vec*side_select, N);"
142*c8dee2aaSAndroid Build Coastguard Worker "half s_base = max(dot(normalize(EYE), LR), 0),"
143*c8dee2aaSAndroid Build Coastguard Worker
144*c8dee2aaSAndroid Build Coastguard Worker "a = l_coeff_ambient,"
145*c8dee2aaSAndroid Build Coastguard Worker "d = l_coeff_diffuse * max(dot(l_vec, N), 0),"
146*c8dee2aaSAndroid Build Coastguard Worker "s = l_coeff_specular * saturate(pow(s_base, l_specular_exp));"
147*c8dee2aaSAndroid Build Coastguard Worker
148*c8dee2aaSAndroid Build Coastguard Worker "c.rgb = (a + d*l_color)*c.rgb + s*l_color*c.a;"
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker "return c;"
151*c8dee2aaSAndroid Build Coastguard Worker "}"
152*c8dee2aaSAndroid Build Coastguard Worker ;
153*c8dee2aaSAndroid Build Coastguard Worker
sphere_fancylight_effect()154*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkRuntimeEffect> sphere_fancylight_effect() {
155*c8dee2aaSAndroid Build Coastguard Worker static const SkRuntimeEffect* effect =
156*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffect::MakeForShader(SkStringPrintf(gSphereSkSL, gFancyLightSkSL), {})
157*c8dee2aaSAndroid Build Coastguard Worker .effect.release();
158*c8dee2aaSAndroid Build Coastguard Worker if (0 && !effect) {
159*c8dee2aaSAndroid Build Coastguard Worker printf("!!! %s\n",
160*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffect::MakeForShader(SkStringPrintf(gSphereSkSL, gFancyLightSkSL), {})
161*c8dee2aaSAndroid Build Coastguard Worker .errorText.c_str());
162*c8dee2aaSAndroid Build Coastguard Worker }
163*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(effect);
164*c8dee2aaSAndroid Build Coastguard Worker
165*c8dee2aaSAndroid Build Coastguard Worker return sk_ref_sp(effect);
166*c8dee2aaSAndroid Build Coastguard Worker }
167*c8dee2aaSAndroid Build Coastguard Worker
sphere_basiclight_effect()168*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkRuntimeEffect> sphere_basiclight_effect() {
169*c8dee2aaSAndroid Build Coastguard Worker static const SkRuntimeEffect* effect =
170*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffect::MakeForShader(SkStringPrintf(gSphereSkSL, gBasicLightSkSL), {})
171*c8dee2aaSAndroid Build Coastguard Worker .effect.release();
172*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(effect);
173*c8dee2aaSAndroid Build Coastguard Worker
174*c8dee2aaSAndroid Build Coastguard Worker return sk_ref_sp(effect);
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker class SphereNode final : public sksg::CustomRenderNode {
178*c8dee2aaSAndroid Build Coastguard Worker public:
SphereNode(sk_sp<RenderNode> child,const SkSize & child_size)179*c8dee2aaSAndroid Build Coastguard Worker SphereNode(sk_sp<RenderNode> child, const SkSize& child_size)
180*c8dee2aaSAndroid Build Coastguard Worker : INHERITED({std::move(child)})
181*c8dee2aaSAndroid Build Coastguard Worker , fChildSize(child_size) {}
182*c8dee2aaSAndroid Build Coastguard Worker
183*c8dee2aaSAndroid Build Coastguard Worker enum class RenderSide {
184*c8dee2aaSAndroid Build Coastguard Worker kFull,
185*c8dee2aaSAndroid Build Coastguard Worker kOutside,
186*c8dee2aaSAndroid Build Coastguard Worker kInside,
187*c8dee2aaSAndroid Build Coastguard Worker };
188*c8dee2aaSAndroid Build Coastguard Worker
189*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(Center , SkPoint , fCenter)
190*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(Radius , float , fRadius)
191*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(Rotation, SkM44 , fRot )
192*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(Side , RenderSide, fSide )
193*c8dee2aaSAndroid Build Coastguard Worker
194*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(LightVec , SkV3 , fLightVec )
195*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(LightColor , SkV3 , fLightColor )
196*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(AmbientLight , float, fAmbientLight )
197*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(DiffuseLight , float, fDiffuseLight )
198*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(SpecularLight, float, fSpecularLight)
199*c8dee2aaSAndroid Build Coastguard Worker SG_ATTRIBUTE(SpecularExp , float, fSpecularExp )
200*c8dee2aaSAndroid Build Coastguard Worker
201*c8dee2aaSAndroid Build Coastguard Worker private:
contentShader()202*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> contentShader() {
203*c8dee2aaSAndroid Build Coastguard Worker if (!fContentShader || this->hasChildrenInval()) {
204*c8dee2aaSAndroid Build Coastguard Worker const auto& child = this->children()[0];
205*c8dee2aaSAndroid Build Coastguard Worker child->revalidate(nullptr, SkMatrix::I());
206*c8dee2aaSAndroid Build Coastguard Worker
207*c8dee2aaSAndroid Build Coastguard Worker SkPictureRecorder recorder;
208*c8dee2aaSAndroid Build Coastguard Worker child->render(recorder.beginRecording(SkRect::MakeSize(fChildSize)));
209*c8dee2aaSAndroid Build Coastguard Worker
210*c8dee2aaSAndroid Build Coastguard Worker fContentShader = recorder.finishRecordingAsPicture()
211*c8dee2aaSAndroid Build Coastguard Worker ->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkFilterMode::kLinear,
212*c8dee2aaSAndroid Build Coastguard Worker nullptr, nullptr);
213*c8dee2aaSAndroid Build Coastguard Worker }
214*c8dee2aaSAndroid Build Coastguard Worker
215*c8dee2aaSAndroid Build Coastguard Worker return fContentShader;
216*c8dee2aaSAndroid Build Coastguard Worker }
217*c8dee2aaSAndroid Build Coastguard Worker
buildEffectShader(float selector)218*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> buildEffectShader(float selector) {
219*c8dee2aaSAndroid Build Coastguard Worker const auto has_fancy_light =
220*c8dee2aaSAndroid Build Coastguard Worker fLightVec.length() > 0 && (fDiffuseLight > 0 || fSpecularLight > 0);
221*c8dee2aaSAndroid Build Coastguard Worker
222*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShaderBuilder builder(has_fancy_light
223*c8dee2aaSAndroid Build Coastguard Worker ? sphere_fancylight_effect()
224*c8dee2aaSAndroid Build Coastguard Worker : sphere_basiclight_effect());
225*c8dee2aaSAndroid Build Coastguard Worker
226*c8dee2aaSAndroid Build Coastguard Worker builder.child ("child") = this->contentShader();
227*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("child_scale") = fChildSize;
228*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("side_select") = selector;
229*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("rot_matrix") = std::array<float,9>{
230*c8dee2aaSAndroid Build Coastguard Worker fRot.rc(0,0), fRot.rc(0,1), fRot.rc(0,2),
231*c8dee2aaSAndroid Build Coastguard Worker fRot.rc(1,0), fRot.rc(1,1), fRot.rc(1,2),
232*c8dee2aaSAndroid Build Coastguard Worker fRot.rc(2,0), fRot.rc(2,1), fRot.rc(2,2),
233*c8dee2aaSAndroid Build Coastguard Worker };
234*c8dee2aaSAndroid Build Coastguard Worker
235*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("l_coeff_ambient") = fAmbientLight;
236*c8dee2aaSAndroid Build Coastguard Worker
237*c8dee2aaSAndroid Build Coastguard Worker if (has_fancy_light) {
238*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("l_vec") = fLightVec * -selector;
239*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("l_color") = fLightColor;
240*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("l_coeff_diffuse") = fDiffuseLight;
241*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("l_coeff_specular") = fSpecularLight;
242*c8dee2aaSAndroid Build Coastguard Worker builder.uniform("l_specular_exp") = fSpecularExp;
243*c8dee2aaSAndroid Build Coastguard Worker }
244*c8dee2aaSAndroid Build Coastguard Worker
245*c8dee2aaSAndroid Build Coastguard Worker const auto lm = SkMatrix::Translate(fCenter.fX, fCenter.fY) *
246*c8dee2aaSAndroid Build Coastguard Worker SkMatrix::Scale(fRadius, fRadius);
247*c8dee2aaSAndroid Build Coastguard Worker
248*c8dee2aaSAndroid Build Coastguard Worker return builder.makeShader(&lm);
249*c8dee2aaSAndroid Build Coastguard Worker }
250*c8dee2aaSAndroid Build Coastguard Worker
onRevalidate(sksg::InvalidationController * ic,const SkMatrix & ctm)251*c8dee2aaSAndroid Build Coastguard Worker SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
252*c8dee2aaSAndroid Build Coastguard Worker fSphereShader.reset();
253*c8dee2aaSAndroid Build Coastguard Worker if (fSide != RenderSide::kOutside) {
254*c8dee2aaSAndroid Build Coastguard Worker fSphereShader = this->buildEffectShader(1);
255*c8dee2aaSAndroid Build Coastguard Worker }
256*c8dee2aaSAndroid Build Coastguard Worker if (fSide != RenderSide::kInside) {
257*c8dee2aaSAndroid Build Coastguard Worker auto outside = this->buildEffectShader(-1);
258*c8dee2aaSAndroid Build Coastguard Worker fSphereShader = fSphereShader
259*c8dee2aaSAndroid Build Coastguard Worker ? SkShaders::Blend(SkBlendMode::kSrcOver,
260*c8dee2aaSAndroid Build Coastguard Worker std::move(fSphereShader),
261*c8dee2aaSAndroid Build Coastguard Worker std::move(outside))
262*c8dee2aaSAndroid Build Coastguard Worker : std::move(outside);
263*c8dee2aaSAndroid Build Coastguard Worker }
264*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fSphereShader);
265*c8dee2aaSAndroid Build Coastguard Worker
266*c8dee2aaSAndroid Build Coastguard Worker return SkRect::MakeLTRB(fCenter.fX - fRadius,
267*c8dee2aaSAndroid Build Coastguard Worker fCenter.fY - fRadius,
268*c8dee2aaSAndroid Build Coastguard Worker fCenter.fX + fRadius,
269*c8dee2aaSAndroid Build Coastguard Worker fCenter.fY + fRadius);
270*c8dee2aaSAndroid Build Coastguard Worker }
271*c8dee2aaSAndroid Build Coastguard Worker
onRender(SkCanvas * canvas,const RenderContext * ctx) const272*c8dee2aaSAndroid Build Coastguard Worker void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
273*c8dee2aaSAndroid Build Coastguard Worker if (fRadius <= 0) {
274*c8dee2aaSAndroid Build Coastguard Worker return;
275*c8dee2aaSAndroid Build Coastguard Worker }
276*c8dee2aaSAndroid Build Coastguard Worker
277*c8dee2aaSAndroid Build Coastguard Worker SkPaint sphere_paint;
278*c8dee2aaSAndroid Build Coastguard Worker sphere_paint.setAntiAlias(true);
279*c8dee2aaSAndroid Build Coastguard Worker sphere_paint.setShader(fSphereShader);
280*c8dee2aaSAndroid Build Coastguard Worker
281*c8dee2aaSAndroid Build Coastguard Worker canvas->drawCircle(fCenter, fRadius, sphere_paint);
282*c8dee2aaSAndroid Build Coastguard Worker }
283*c8dee2aaSAndroid Build Coastguard Worker
onNodeAt(const SkPoint &) const284*c8dee2aaSAndroid Build Coastguard Worker const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
285*c8dee2aaSAndroid Build Coastguard Worker
286*c8dee2aaSAndroid Build Coastguard Worker const SkSize fChildSize;
287*c8dee2aaSAndroid Build Coastguard Worker
288*c8dee2aaSAndroid Build Coastguard Worker // Cached shaders
289*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> fSphereShader;
290*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkShader> fContentShader;
291*c8dee2aaSAndroid Build Coastguard Worker
292*c8dee2aaSAndroid Build Coastguard Worker // Effect controls.
293*c8dee2aaSAndroid Build Coastguard Worker SkM44 fRot;
294*c8dee2aaSAndroid Build Coastguard Worker SkPoint fCenter = {0,0};
295*c8dee2aaSAndroid Build Coastguard Worker float fRadius = 0;
296*c8dee2aaSAndroid Build Coastguard Worker RenderSide fSide = RenderSide::kFull;
297*c8dee2aaSAndroid Build Coastguard Worker
298*c8dee2aaSAndroid Build Coastguard Worker SkV3 fLightVec = {0,0,1},
299*c8dee2aaSAndroid Build Coastguard Worker fLightColor = {1,1,1};
300*c8dee2aaSAndroid Build Coastguard Worker float fAmbientLight = 1,
301*c8dee2aaSAndroid Build Coastguard Worker fDiffuseLight = 0,
302*c8dee2aaSAndroid Build Coastguard Worker fSpecularLight = 0,
303*c8dee2aaSAndroid Build Coastguard Worker fSpecularExp = 0;
304*c8dee2aaSAndroid Build Coastguard Worker
305*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = sksg::CustomRenderNode;
306*c8dee2aaSAndroid Build Coastguard Worker };
307*c8dee2aaSAndroid Build Coastguard Worker
308*c8dee2aaSAndroid Build Coastguard Worker class SphereAdapter final : public DiscardableAdapterBase<SphereAdapter, SphereNode> {
309*c8dee2aaSAndroid Build Coastguard Worker public:
SphereAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder * abuilder,sk_sp<SphereNode> node)310*c8dee2aaSAndroid Build Coastguard Worker SphereAdapter(const skjson::ArrayValue& jprops,
311*c8dee2aaSAndroid Build Coastguard Worker const AnimationBuilder* abuilder,
312*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SphereNode> node)
313*c8dee2aaSAndroid Build Coastguard Worker : INHERITED(std::move(node))
314*c8dee2aaSAndroid Build Coastguard Worker {
315*c8dee2aaSAndroid Build Coastguard Worker enum : size_t {
316*c8dee2aaSAndroid Build Coastguard Worker // kRotGrp_Index = 0,
317*c8dee2aaSAndroid Build Coastguard Worker kRotX_Index = 1,
318*c8dee2aaSAndroid Build Coastguard Worker kRotY_Index = 2,
319*c8dee2aaSAndroid Build Coastguard Worker kRotZ_Index = 3,
320*c8dee2aaSAndroid Build Coastguard Worker kRotOrder_Index = 4,
321*c8dee2aaSAndroid Build Coastguard Worker // ??? = 5,
322*c8dee2aaSAndroid Build Coastguard Worker kRadius_Index = 6,
323*c8dee2aaSAndroid Build Coastguard Worker kOffset_Index = 7,
324*c8dee2aaSAndroid Build Coastguard Worker kRender_Index = 8,
325*c8dee2aaSAndroid Build Coastguard Worker
326*c8dee2aaSAndroid Build Coastguard Worker // kLight_Index = 9,
327*c8dee2aaSAndroid Build Coastguard Worker kLightIntensity_Index = 10,
328*c8dee2aaSAndroid Build Coastguard Worker kLightColor_Index = 11,
329*c8dee2aaSAndroid Build Coastguard Worker kLightHeight_Index = 12,
330*c8dee2aaSAndroid Build Coastguard Worker kLightDirection_Index = 13,
331*c8dee2aaSAndroid Build Coastguard Worker // ??? = 14,
332*c8dee2aaSAndroid Build Coastguard Worker // kShading_Index = 15,
333*c8dee2aaSAndroid Build Coastguard Worker kAmbient_Index = 16,
334*c8dee2aaSAndroid Build Coastguard Worker kDiffuse_Index = 17,
335*c8dee2aaSAndroid Build Coastguard Worker kSpecular_Index = 18,
336*c8dee2aaSAndroid Build Coastguard Worker kRoughness_Index = 19,
337*c8dee2aaSAndroid Build Coastguard Worker };
338*c8dee2aaSAndroid Build Coastguard Worker
339*c8dee2aaSAndroid Build Coastguard Worker EffectBinder(jprops, *abuilder, this)
340*c8dee2aaSAndroid Build Coastguard Worker .bind( kOffset_Index, fOffset )
341*c8dee2aaSAndroid Build Coastguard Worker .bind( kRadius_Index, fRadius )
342*c8dee2aaSAndroid Build Coastguard Worker .bind( kRotX_Index, fRotX )
343*c8dee2aaSAndroid Build Coastguard Worker .bind( kRotY_Index, fRotY )
344*c8dee2aaSAndroid Build Coastguard Worker .bind( kRotZ_Index, fRotZ )
345*c8dee2aaSAndroid Build Coastguard Worker .bind(kRotOrder_Index, fRotOrder)
346*c8dee2aaSAndroid Build Coastguard Worker .bind( kRender_Index, fRender )
347*c8dee2aaSAndroid Build Coastguard Worker
348*c8dee2aaSAndroid Build Coastguard Worker .bind(kLightIntensity_Index, fLightIntensity)
349*c8dee2aaSAndroid Build Coastguard Worker .bind( kLightColor_Index, fLightColor )
350*c8dee2aaSAndroid Build Coastguard Worker .bind( kLightHeight_Index, fLightHeight )
351*c8dee2aaSAndroid Build Coastguard Worker .bind(kLightDirection_Index, fLightDirection)
352*c8dee2aaSAndroid Build Coastguard Worker .bind( kAmbient_Index, fAmbient )
353*c8dee2aaSAndroid Build Coastguard Worker .bind( kDiffuse_Index, fDiffuse )
354*c8dee2aaSAndroid Build Coastguard Worker .bind( kSpecular_Index, fSpecular )
355*c8dee2aaSAndroid Build Coastguard Worker .bind( kRoughness_Index, fRoughness );
356*c8dee2aaSAndroid Build Coastguard Worker }
357*c8dee2aaSAndroid Build Coastguard Worker
358*c8dee2aaSAndroid Build Coastguard Worker private:
onSync()359*c8dee2aaSAndroid Build Coastguard Worker void onSync() override {
360*c8dee2aaSAndroid Build Coastguard Worker const auto side = [](ScalarValue s) {
361*c8dee2aaSAndroid Build Coastguard Worker switch (SkScalarRoundToInt(s)) {
362*c8dee2aaSAndroid Build Coastguard Worker case 1: return SphereNode::RenderSide::kFull;
363*c8dee2aaSAndroid Build Coastguard Worker case 2: return SphereNode::RenderSide::kOutside;
364*c8dee2aaSAndroid Build Coastguard Worker case 3:
365*c8dee2aaSAndroid Build Coastguard Worker default: return SphereNode::RenderSide::kInside;
366*c8dee2aaSAndroid Build Coastguard Worker }
367*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
368*c8dee2aaSAndroid Build Coastguard Worker };
369*c8dee2aaSAndroid Build Coastguard Worker
370*c8dee2aaSAndroid Build Coastguard Worker const auto rotation = [](ScalarValue order,
371*c8dee2aaSAndroid Build Coastguard Worker ScalarValue x, ScalarValue y, ScalarValue z) {
372*c8dee2aaSAndroid Build Coastguard Worker const SkM44 rx = SkM44::Rotate({1,0,0}, SkDegreesToRadians( x)),
373*c8dee2aaSAndroid Build Coastguard Worker ry = SkM44::Rotate({0,1,0}, SkDegreesToRadians( y)),
374*c8dee2aaSAndroid Build Coastguard Worker rz = SkM44::Rotate({0,0,1}, SkDegreesToRadians(-z));
375*c8dee2aaSAndroid Build Coastguard Worker
376*c8dee2aaSAndroid Build Coastguard Worker switch (SkScalarRoundToInt(order)) {
377*c8dee2aaSAndroid Build Coastguard Worker case 1: return rx * ry * rz;
378*c8dee2aaSAndroid Build Coastguard Worker case 2: return rx * rz * ry;
379*c8dee2aaSAndroid Build Coastguard Worker case 3: return ry * rx * rz;
380*c8dee2aaSAndroid Build Coastguard Worker case 4: return ry * rz * rx;
381*c8dee2aaSAndroid Build Coastguard Worker case 5: return rz * rx * ry;
382*c8dee2aaSAndroid Build Coastguard Worker case 6:
383*c8dee2aaSAndroid Build Coastguard Worker default: return rz * ry * rx;
384*c8dee2aaSAndroid Build Coastguard Worker }
385*c8dee2aaSAndroid Build Coastguard Worker SkUNREACHABLE;
386*c8dee2aaSAndroid Build Coastguard Worker };
387*c8dee2aaSAndroid Build Coastguard Worker
388*c8dee2aaSAndroid Build Coastguard Worker const auto light_vec = [](float height, float direction) {
389*c8dee2aaSAndroid Build Coastguard Worker float z = std::sin(height * SK_ScalarPI / 2),
390*c8dee2aaSAndroid Build Coastguard Worker r = std::sqrt(1 - z*z),
391*c8dee2aaSAndroid Build Coastguard Worker x = std::cos(direction) * r,
392*c8dee2aaSAndroid Build Coastguard Worker y = std::sin(direction) * r;
393*c8dee2aaSAndroid Build Coastguard Worker
394*c8dee2aaSAndroid Build Coastguard Worker return SkV3{x,y,z};
395*c8dee2aaSAndroid Build Coastguard Worker };
396*c8dee2aaSAndroid Build Coastguard Worker
397*c8dee2aaSAndroid Build Coastguard Worker const auto& sph = this->node();
398*c8dee2aaSAndroid Build Coastguard Worker
399*c8dee2aaSAndroid Build Coastguard Worker sph->setCenter({fOffset.x, fOffset.y});
400*c8dee2aaSAndroid Build Coastguard Worker sph->setRadius(fRadius);
401*c8dee2aaSAndroid Build Coastguard Worker sph->setSide(side(fRender));
402*c8dee2aaSAndroid Build Coastguard Worker sph->setRotation(rotation(fRotOrder, fRotX, fRotY, fRotZ));
403*c8dee2aaSAndroid Build Coastguard Worker
404*c8dee2aaSAndroid Build Coastguard Worker sph->setAmbientLight (SkTPin(fAmbient * 0.01f, 0.0f, 2.0f));
405*c8dee2aaSAndroid Build Coastguard Worker
406*c8dee2aaSAndroid Build Coastguard Worker const auto intensity = SkTPin(fLightIntensity * 0.01f, 0.0f, 10.0f);
407*c8dee2aaSAndroid Build Coastguard Worker sph->setDiffuseLight (SkTPin(fDiffuse * 0.01f, 0.0f, 1.0f) * intensity);
408*c8dee2aaSAndroid Build Coastguard Worker sph->setSpecularLight(SkTPin(fSpecular* 0.01f, 0.0f, 1.0f) * intensity);
409*c8dee2aaSAndroid Build Coastguard Worker
410*c8dee2aaSAndroid Build Coastguard Worker sph->setLightVec(light_vec(
411*c8dee2aaSAndroid Build Coastguard Worker SkTPin(fLightHeight * 0.01f, -1.0f, 1.0f),
412*c8dee2aaSAndroid Build Coastguard Worker SkDegreesToRadians(fLightDirection - 90)
413*c8dee2aaSAndroid Build Coastguard Worker ));
414*c8dee2aaSAndroid Build Coastguard Worker
415*c8dee2aaSAndroid Build Coastguard Worker const auto lc = static_cast<SkColor4f>(fLightColor);
416*c8dee2aaSAndroid Build Coastguard Worker sph->setLightColor({lc.fR, lc.fG, lc.fB});
417*c8dee2aaSAndroid Build Coastguard Worker
418*c8dee2aaSAndroid Build Coastguard Worker sph->setSpecularExp(1/SkTPin(fRoughness, 0.001f, 0.5f));
419*c8dee2aaSAndroid Build Coastguard Worker }
420*c8dee2aaSAndroid Build Coastguard Worker
421*c8dee2aaSAndroid Build Coastguard Worker Vec2Value fOffset = {0,0};
422*c8dee2aaSAndroid Build Coastguard Worker ScalarValue fRadius = 0,
423*c8dee2aaSAndroid Build Coastguard Worker fRotX = 0,
424*c8dee2aaSAndroid Build Coastguard Worker fRotY = 0,
425*c8dee2aaSAndroid Build Coastguard Worker fRotZ = 0,
426*c8dee2aaSAndroid Build Coastguard Worker fRotOrder = 1,
427*c8dee2aaSAndroid Build Coastguard Worker fRender = 1;
428*c8dee2aaSAndroid Build Coastguard Worker
429*c8dee2aaSAndroid Build Coastguard Worker ColorValue fLightColor;
430*c8dee2aaSAndroid Build Coastguard Worker ScalarValue fLightIntensity = 0,
431*c8dee2aaSAndroid Build Coastguard Worker fLightHeight = 0,
432*c8dee2aaSAndroid Build Coastguard Worker fLightDirection = 0,
433*c8dee2aaSAndroid Build Coastguard Worker fAmbient = 100,
434*c8dee2aaSAndroid Build Coastguard Worker fDiffuse = 0,
435*c8dee2aaSAndroid Build Coastguard Worker fSpecular = 0,
436*c8dee2aaSAndroid Build Coastguard Worker fRoughness = 0.5f;
437*c8dee2aaSAndroid Build Coastguard Worker
438*c8dee2aaSAndroid Build Coastguard Worker using INHERITED = DiscardableAdapterBase<SphereAdapter, SphereNode>;
439*c8dee2aaSAndroid Build Coastguard Worker };
440*c8dee2aaSAndroid Build Coastguard Worker
441*c8dee2aaSAndroid Build Coastguard Worker } // namespace
442*c8dee2aaSAndroid Build Coastguard Worker
attachSphereEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const443*c8dee2aaSAndroid Build Coastguard Worker sk_sp<sksg::RenderNode> EffectBuilder::attachSphereEffect(
444*c8dee2aaSAndroid Build Coastguard Worker const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const {
445*c8dee2aaSAndroid Build Coastguard Worker auto sphere = sk_make_sp<SphereNode>(std::move(layer), fLayerSize);
446*c8dee2aaSAndroid Build Coastguard Worker
447*c8dee2aaSAndroid Build Coastguard Worker return fBuilder->attachDiscardableAdapter<SphereAdapter>(jprops, fBuilder, std::move(sphere));
448*c8dee2aaSAndroid Build Coastguard Worker }
449*c8dee2aaSAndroid Build Coastguard Worker
450*c8dee2aaSAndroid Build Coastguard Worker } // namespace skottie::internal
451