1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2020 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "modules/skottie/src/Camera.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "modules/skottie/src/SkottieJson.h"
11cb93a386Sopenharmony_ci#include "modules/skottie/src/SkottiePriv.h"
12cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGTransform.h"
13cb93a386Sopenharmony_ci
14cb93a386Sopenharmony_cinamespace skottie {
15cb93a386Sopenharmony_cinamespace internal {
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_cinamespace  {
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ciSkM44 ComputeCameraMatrix(const SkV3& position,
20cb93a386Sopenharmony_ci                          const SkV3& poi,
21cb93a386Sopenharmony_ci                          const SkV3& rotation,
22cb93a386Sopenharmony_ci                          const SkSize& viewport_size,
23cb93a386Sopenharmony_ci                          float zoom) {
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci    // Initial camera vector.
26cb93a386Sopenharmony_ci    const auto cam_t = SkM44::Rotate({0, 0, 1}, SkDegreesToRadians(-rotation.z))
27cb93a386Sopenharmony_ci                     * SkM44::Rotate({0, 1, 0}, SkDegreesToRadians( rotation.y))
28cb93a386Sopenharmony_ci                     * SkM44::Rotate({1, 0, 0}, SkDegreesToRadians( rotation.x))
29cb93a386Sopenharmony_ci                     * SkM44::LookAt({ position.x, position.y, -position.z },
30cb93a386Sopenharmony_ci                                     {      poi.x,      poi.y,       poi.z },
31cb93a386Sopenharmony_ci                                     {          0,          1,           0 })
32cb93a386Sopenharmony_ci                     * SkM44::Scale(1, 1, -1);
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_ci    // View parameters:
35cb93a386Sopenharmony_ci    //
36cb93a386Sopenharmony_ci    //   * size     -> composition size (TODO: AE seems to base it on width only?)
37cb93a386Sopenharmony_ci    //   * distance -> "zoom" camera attribute
38cb93a386Sopenharmony_ci    //
39cb93a386Sopenharmony_ci    const auto view_size     = std::max(viewport_size.width(), viewport_size.height()),
40cb93a386Sopenharmony_ci               view_distance = zoom,
41cb93a386Sopenharmony_ci               view_angle    = std::atan(sk_ieee_float_divide(view_size * 0.5f, view_distance));
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_ci    const auto persp_t = SkM44::Scale(view_size * 0.5f, view_size * 0.5f, 1)
44cb93a386Sopenharmony_ci                       * SkM44::Perspective(0, view_distance, 2 * view_angle);
45cb93a386Sopenharmony_ci
46cb93a386Sopenharmony_ci    return SkM44::Translate(viewport_size.width()  * 0.5f,
47cb93a386Sopenharmony_ci                            viewport_size.height() * 0.5f,
48cb93a386Sopenharmony_ci                            0)
49cb93a386Sopenharmony_ci           * persp_t * cam_t;
50cb93a386Sopenharmony_ci}
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci} // namespace
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_ciCameraAdaper::CameraAdaper(const skjson::ObjectValue& jlayer,
55cb93a386Sopenharmony_ci                           const skjson::ObjectValue& jtransform,
56cb93a386Sopenharmony_ci                           const AnimationBuilder& abuilder,
57cb93a386Sopenharmony_ci                           const SkSize& viewport_size)
58cb93a386Sopenharmony_ci    : INHERITED(jtransform, abuilder)
59cb93a386Sopenharmony_ci    , fViewportSize(viewport_size)
60cb93a386Sopenharmony_ci    // The presence of an anchor point property ('a') differentiates
61cb93a386Sopenharmony_ci    // one-node vs. two-node cameras.
62cb93a386Sopenharmony_ci    , fType(jtransform["a"].is<skjson::NullValue>() ? CameraType::kOneNode
63cb93a386Sopenharmony_ci                                                    : CameraType::kTwoNode) {
64cb93a386Sopenharmony_ci    // 'pe' (perspective?) corresponds to AE's "zoom" camera property.
65cb93a386Sopenharmony_ci    this->bind(abuilder, jlayer["pe"], fZoom);
66cb93a386Sopenharmony_ci}
67cb93a386Sopenharmony_ci
68cb93a386Sopenharmony_ciCameraAdaper::~CameraAdaper() = default;
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ciSkM44 CameraAdaper::totalMatrix() const {
71cb93a386Sopenharmony_ci    // Camera parameters:
72cb93a386Sopenharmony_ci    //
73cb93a386Sopenharmony_ci    //   * location          -> position attribute
74cb93a386Sopenharmony_ci    //   * point of interest -> anchor point attribute (two-node camera only)
75cb93a386Sopenharmony_ci    //   * orientation       -> rotation attribute
76cb93a386Sopenharmony_ci    //
77cb93a386Sopenharmony_ci    const auto position = this->position();
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci    return ComputeCameraMatrix(position,
80cb93a386Sopenharmony_ci                               this->poi(position),
81cb93a386Sopenharmony_ci                               this->rotation(),
82cb93a386Sopenharmony_ci                               fViewportSize,
83cb93a386Sopenharmony_ci                               fZoom);
84cb93a386Sopenharmony_ci}
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_ciSkV3 CameraAdaper::poi(const SkV3& pos) const {
87cb93a386Sopenharmony_ci    // AE supports two camera types:
88cb93a386Sopenharmony_ci    //
89cb93a386Sopenharmony_ci    //   - one-node camera: does not auto-orient, and starts off perpendicular
90cb93a386Sopenharmony_ci    //     to the z = 0 plane, facing "forward" (decreasing z).
91cb93a386Sopenharmony_ci    //
92cb93a386Sopenharmony_ci    //   - two-node camera: has a point of interest (encoded as the anchor point),
93cb93a386Sopenharmony_ci    //     and auto-orients to point in its direction.
94cb93a386Sopenharmony_ci
95cb93a386Sopenharmony_ci    if (fType == CameraType::kOneNode) {
96cb93a386Sopenharmony_ci        return { pos.x, pos.y, -pos.z - 1};
97cb93a386Sopenharmony_ci    }
98cb93a386Sopenharmony_ci
99cb93a386Sopenharmony_ci    const auto ap = this->anchor_point();
100cb93a386Sopenharmony_ci
101cb93a386Sopenharmony_ci    return { ap.x, ap.y, -ap.z };
102cb93a386Sopenharmony_ci}
103cb93a386Sopenharmony_ci
104cb93a386Sopenharmony_cisk_sp<sksg::Transform> CameraAdaper::DefaultCameraTransform(const SkSize& viewport_size) {
105cb93a386Sopenharmony_ci    const auto center = SkVector::Make(viewport_size.width()  * 0.5f,
106cb93a386Sopenharmony_ci                                       viewport_size.height() * 0.5f);
107cb93a386Sopenharmony_ci
108cb93a386Sopenharmony_ci    static constexpr float kDefaultAEZoom = 879.13f;
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci    const SkV3 pos = { center.fX, center.fY, -kDefaultAEZoom },
111cb93a386Sopenharmony_ci               poi = {     pos.x,     pos.y,      -pos.z - 1 },
112cb93a386Sopenharmony_ci               rot = {         0,         0,               0 };
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    return sksg::Matrix<SkM44>::Make(
115cb93a386Sopenharmony_ci                ComputeCameraMatrix(pos, poi, rot, viewport_size, kDefaultAEZoom));
116cb93a386Sopenharmony_ci}
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_cisk_sp<sksg::Transform> AnimationBuilder::attachCamera(const skjson::ObjectValue& jlayer,
119cb93a386Sopenharmony_ci                                                      const skjson::ObjectValue& jtransform,
120cb93a386Sopenharmony_ci                                                      sk_sp<sksg::Transform> parent,
121cb93a386Sopenharmony_ci                                                      const SkSize& viewport_size) const {
122cb93a386Sopenharmony_ci    auto adapter = sk_make_sp<CameraAdaper>(jlayer, jtransform, *this, viewport_size);
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci    if (adapter->isStatic()) {
125cb93a386Sopenharmony_ci        adapter->seek(0);
126cb93a386Sopenharmony_ci    } else {
127cb93a386Sopenharmony_ci        fCurrentAnimatorScope->push_back(adapter);
128cb93a386Sopenharmony_ci    }
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci    return sksg::Transform::MakeConcat(adapter->node(), std::move(parent));
131cb93a386Sopenharmony_ci}
132cb93a386Sopenharmony_ci
133cb93a386Sopenharmony_ci} // namespace internal
134cb93a386Sopenharmony_ci} // namespace skottie
135