[skottie] Initial 3D layer transform support

TBR=
Change-Id: Ia849a0062a75863857edc66d8a5c1c62bfc6d00a
Reviewed-on: https://skia-review.googlesource.com/c/180362
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/include/SkottieProperty.h b/modules/skottie/include/SkottieProperty.h
index a738697..a3dee71 100644
--- a/modules/skottie/include/SkottieProperty.h
+++ b/modules/skottie/include/SkottieProperty.h
@@ -62,11 +62,11 @@
     const sk_sp<NodeT> fNode;
 };
 
-class TransformAdapter;
+class TransformAdapter2D;
 
 using ColorPropertyHandle     = PropertyHandle<ColorPropertyValue    , sksg::Color         >;
 using OpacityPropertyHandle   = PropertyHandle<OpacityPropertyValue  , sksg::OpacityEffect >;
-using TransformPropertyHandle = PropertyHandle<TransformPropertyValue, TransformAdapter    >;
+using TransformPropertyHandle = PropertyHandle<TransformPropertyValue, TransformAdapter2D  >;
 
 /**
  * A PropertyObserver can be used to track and manipulate certain properties of "interesting"
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index a18ffc5..65fed85 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -61,14 +61,14 @@
     fLogger->log(lvl, buff, jsonstr.c_str());
 }
 
-sk_sp<sksg::Matrix> AnimationBuilder::attachMatrix(const skjson::ObjectValue& t,
-                                                   AnimatorScope* ascope,
-                                                   sk_sp<sksg::Matrix> parentMatrix) const {
+sk_sp<sksg::Matrix> AnimationBuilder::attachMatrix2D(const skjson::ObjectValue& t,
+                                                     AnimatorScope* ascope,
+                                                     sk_sp<sksg::Matrix> parentMatrix) const {
     static const VectorValue g_default_vec_0   = {  0,   0},
                              g_default_vec_100 = {100, 100};
 
     auto matrix = sksg::Matrix::Make(SkMatrix::I(), parentMatrix);
-    auto adapter = sk_make_sp<TransformAdapter>(matrix);
+    auto adapter = sk_make_sp<TransformAdapter2D>(matrix);
 
     auto bound = this->bindProperty<VectorValue>(t["a"], ascope,
             [adapter](const VectorValue& a) {
@@ -107,6 +107,58 @@
     return (bound || dispatched) ? matrix : parentMatrix;
 }
 
+sk_sp<sksg::Matrix> AnimationBuilder::attachMatrix3D(const skjson::ObjectValue& t,
+                                                     AnimatorScope* ascope,
+                                                     sk_sp<sksg::Matrix> parentMatrix) const {
+    static const VectorValue g_default_vec_0   = {  0,   0,   0},
+                             g_default_vec_100 = {100, 100, 100};
+
+    auto matrix = sksg::Matrix::Make(SkMatrix::I(), parentMatrix);
+    auto adapter = sk_make_sp<TransformAdapter3D>(matrix);
+
+    auto bound = this->bindProperty<VectorValue>(t["a"], ascope,
+            [adapter](const VectorValue& a) {
+                adapter->setAnchorPoint(TransformAdapter3D::Vec3(a));
+            }, g_default_vec_0);
+    bound |= this->bindProperty<VectorValue>(t["p"], ascope,
+            [adapter](const VectorValue& p) {
+                adapter->setPosition(TransformAdapter3D::Vec3(p));
+            }, g_default_vec_0);
+    bound |= this->bindProperty<VectorValue>(t["s"], ascope,
+            [adapter](const VectorValue& s) {
+                adapter->setScale(TransformAdapter3D::Vec3(s));
+            }, g_default_vec_100);
+
+    // Orientation and rx/ry/rz are mapped to the same rotation property -- the difference is
+    // in how they get interpolated (vector vs. scalar/decomposed interpolation).
+    bound |= this->bindProperty<VectorValue>(t["or"], ascope,
+            [adapter](const VectorValue& o) {
+                adapter->setRotation(TransformAdapter3D::Vec3(o));
+            }, g_default_vec_0);
+
+    bound |= this->bindProperty<ScalarValue>(t["rx"], ascope,
+            [adapter](const ScalarValue& rx) {
+                const auto& r = adapter->getRotation();
+                adapter->setRotation(TransformAdapter3D::Vec3({rx, r.fY, r.fZ}));
+            }, 0.0f);
+
+    bound |= this->bindProperty<ScalarValue>(t["ry"], ascope,
+            [adapter](const ScalarValue& ry) {
+                const auto& r = adapter->getRotation();
+                adapter->setRotation(TransformAdapter3D::Vec3({r.fX, ry, r.fZ}));
+            }, 0.0f);
+
+    bound |= this->bindProperty<ScalarValue>(t["rz"], ascope,
+            [adapter](const ScalarValue& rz) {
+                const auto& r = adapter->getRotation();
+                adapter->setRotation(TransformAdapter3D::Vec3({r.fX, r.fY, rz}));
+            }, 0.0f);
+
+    // TODO: dispatch 3D transform properties
+
+    return bound ? matrix : parentMatrix;
+}
+
 sk_sp<sksg::RenderNode> AnimationBuilder::attachOpacity(const skjson::ObjectValue& jtransform,
                                                         AnimatorScope* ascope,
                                                         sk_sp<sksg::RenderNode> childNode) const {
@@ -251,7 +303,7 @@
     return dispatched;
 }
 
-bool AnimationBuilder::dispatchTransformProperty(const sk_sp<TransformAdapter>& t) const {
+bool AnimationBuilder::dispatchTransformProperty(const sk_sp<TransformAdapter2D>& t) const {
     bool dispatched = false;
 
     if (fPropertyObserver) {
diff --git a/modules/skottie/src/SkottieAdapter.cpp b/modules/skottie/src/SkottieAdapter.cpp
index 0dd51bb..0d53869 100644
--- a/modules/skottie/src/SkottieAdapter.cpp
+++ b/modules/skottie/src/SkottieAdapter.cpp
@@ -9,6 +9,7 @@
 
 #include "SkFont.h"
 #include "SkMatrix.h"
+#include "SkMatrix44.h"
 #include "SkPath.h"
 #include "SkRRect.h"
 #include "SkSGColor.h"
@@ -46,12 +47,12 @@
    fRRectNode->setRRect(rr);
 }
 
-TransformAdapter::TransformAdapter(sk_sp<sksg::Matrix> matrix)
+TransformAdapter2D::TransformAdapter2D(sk_sp<sksg::Matrix> matrix)
     : fMatrixNode(std::move(matrix)) {}
 
-TransformAdapter::~TransformAdapter() = default;
+TransformAdapter2D::~TransformAdapter2D() = default;
 
-SkMatrix TransformAdapter::totalMatrix() const {
+SkMatrix TransformAdapter2D::totalMatrix() const {
     SkMatrix t = SkMatrix::MakeTrans(-fAnchorPoint.x(), -fAnchorPoint.y());
 
     t.postScale(fScale.x() / 100, fScale.y() / 100); // 100% based
@@ -62,7 +63,42 @@
     return t;
 }
 
-void TransformAdapter::apply() {
+void TransformAdapter2D::apply() {
+    fMatrixNode->setMatrix(this->totalMatrix());
+}
+
+TransformAdapter3D::Vec3::Vec3(const VectorValue& v) {
+    fX = v.size() > 0 ? v[0] : 0;
+    fY = v.size() > 1 ? v[1] : 0;
+    fZ = v.size() > 2 ? v[2] : 0;
+}
+
+TransformAdapter3D::TransformAdapter3D(sk_sp<sksg::Matrix> matrix)
+    : fMatrixNode(std::move(matrix)) {}
+
+TransformAdapter3D::~TransformAdapter3D() = default;
+
+SkMatrix TransformAdapter3D::totalMatrix() const {
+    SkMatrix44 t;
+
+    t.setTranslate(-fAnchorPoint.fX, -fAnchorPoint.fY, -fAnchorPoint.fZ);
+    t.postScale(fScale.fX / 100, fScale.fY / 100, fScale.fZ / 100);
+
+    // TODO: SkMatrix44:postRotate()?
+    SkMatrix44 r;
+    r.setRotateDegreesAbout(1, 0, 0, fRotation.fX);
+    t.postConcat(r);
+    r.setRotateDegreesAbout(0, 1, 0, fRotation.fY);
+    t.postConcat(r);
+    r.setRotateDegreesAbout(0, 0, 1, fRotation.fZ);
+    t.postConcat(r);
+
+    t.postTranslate(fPosition.fX, fPosition.fY, fPosition.fZ);
+
+    return t;
+}
+
+void TransformAdapter3D::apply() {
     fMatrixNode->setMatrix(this->totalMatrix());
 }
 
diff --git a/modules/skottie/src/SkottieAdapter.h b/modules/skottie/src/SkottieAdapter.h
index 13b74f2..1ab9a7b 100644
--- a/modules/skottie/src/SkottieAdapter.h
+++ b/modules/skottie/src/SkottieAdapter.h
@@ -83,10 +83,10 @@
     Type              fType;
 };
 
-class TransformAdapter final : public SkNVRefCnt<TransformAdapter> {
+class TransformAdapter2D final : public SkNVRefCnt<TransformAdapter2D> {
 public:
-    explicit TransformAdapter(sk_sp<sksg::Matrix>);
-    ~TransformAdapter();
+    explicit TransformAdapter2D(sk_sp<sksg::Matrix>);
+    ~TransformAdapter2D();
 
     ADAPTER_PROPERTY(AnchorPoint, SkPoint , SkPoint::Make(0, 0))
     ADAPTER_PROPERTY(Position   , SkPoint , SkPoint::Make(0, 0))
@@ -103,6 +103,35 @@
     sk_sp<sksg::Matrix> fMatrixNode;
 };
 
+class TransformAdapter3D final : public SkNVRefCnt<TransformAdapter3D> {
+public:
+    explicit TransformAdapter3D(sk_sp<sksg::Matrix>);
+    ~TransformAdapter3D();
+
+    struct Vec3 {
+        float fX, fY, fZ;
+
+        explicit Vec3(const VectorValue&);
+
+        bool operator==(const Vec3& other) const {
+            return fX == other.fX && fY == other.fY && fZ == other.fZ;
+        }
+        bool operator!=(const Vec3& other) const { return !(*this == other); }
+    };
+
+    ADAPTER_PROPERTY(AnchorPoint, Vec3, Vec3({  0,   0,   0}))
+    ADAPTER_PROPERTY(Position   , Vec3, Vec3({  0,   0,   0}))
+    ADAPTER_PROPERTY(Rotation   , Vec3, Vec3({  0,   0,   0}))
+    ADAPTER_PROPERTY(Scale      , Vec3, Vec3({100, 100, 100}))
+
+    SkMatrix totalMatrix() const;
+
+private:
+    void apply();
+
+    sk_sp<sksg::Matrix> fMatrixNode;
+};
+
 class GradientAdapter : public SkRefCnt {
 public:
     ADAPTER_PROPERTY(StartPoint, SkPoint        , SkPoint::Make(0, 0)   )
diff --git a/modules/skottie/src/SkottieLayer.cpp b/modules/skottie/src/SkottieLayer.cpp
index 2786ed5..2bb6264 100644
--- a/modules/skottie/src/SkottieLayer.cpp
+++ b/modules/skottie/src/SkottieLayer.cpp
@@ -449,10 +449,11 @@
         auto parent_matrix = this->AttachParentLayerMatrix(jlayer, abuilder, layer_index);
 
         if (const skjson::ObjectValue* jtransform = jlayer["ks"]) {
-            return *fLayerMatrixMap.set(layer_index,
-                                        abuilder->attachMatrix(*jtransform, fScope,
-                                                               std::move(parent_matrix)));
+            auto matrix_node = (ParseDefault<int>(jlayer["ddd"], 0) == 0)
+                ? abuilder->attachMatrix2D(*jtransform, fScope, std::move(parent_matrix))
+                : abuilder->attachMatrix3D(*jtransform, fScope, std::move(parent_matrix));
 
+            return *fLayerMatrixMap.set(layer_index, std::move(matrix_node));
         }
         return nullptr;
     }
diff --git a/modules/skottie/src/SkottiePriv.h b/modules/skottie/src/SkottiePriv.h
index b3c15e5..ee41f37 100644
--- a/modules/skottie/src/SkottiePriv.h
+++ b/modules/skottie/src/SkottiePriv.h
@@ -71,8 +71,10 @@
 
     sk_sp<sksg::Color> attachColor(const skjson::ObjectValue&, AnimatorScope*,
                                    const char prop_name[]) const;
-    sk_sp<sksg::Matrix> attachMatrix(const skjson::ObjectValue&, AnimatorScope*,
-                                     sk_sp<sksg::Matrix>) const;
+    sk_sp<sksg::Matrix> attachMatrix2D(const skjson::ObjectValue&, AnimatorScope*,
+                                       sk_sp<sksg::Matrix>) const;
+    sk_sp<sksg::Matrix> attachMatrix3D(const skjson::ObjectValue&, AnimatorScope*,
+                                       sk_sp<sksg::Matrix>) const;
     sk_sp<sksg::RenderNode> attachOpacity(const skjson::ObjectValue&, AnimatorScope*,
                                       sk_sp<sksg::RenderNode>) const;
     sk_sp<sksg::Path> attachPath(const skjson::Value&, AnimatorScope*) const;
@@ -119,7 +121,7 @@
 
     bool dispatchColorProperty(const sk_sp<sksg::Color>&) const;
     bool dispatchOpacityProperty(const sk_sp<sksg::OpacityEffect>&) const;
-    bool dispatchTransformProperty(const sk_sp<TransformAdapter>&) const;
+    bool dispatchTransformProperty(const sk_sp<TransformAdapter2D>&) const;
 
     // Delay resolving the fontmgr until it is actually needed.
     struct LazyResolveFontMgr {
diff --git a/modules/skottie/src/SkottieProperty.cpp b/modules/skottie/src/SkottieProperty.cpp
index 0d27017..143b97a 100644
--- a/modules/skottie/src/SkottieProperty.cpp
+++ b/modules/skottie/src/SkottieProperty.cpp
@@ -52,10 +52,10 @@
 }
 
 template <>
-PropertyHandle<TransformPropertyValue, TransformAdapter>::~PropertyHandle() {}
+PropertyHandle<TransformPropertyValue, TransformAdapter2D>::~PropertyHandle() {}
 
 template <>
-TransformPropertyValue PropertyHandle<TransformPropertyValue, TransformAdapter>::get() const {
+TransformPropertyValue PropertyHandle<TransformPropertyValue, TransformAdapter2D>::get() const {
     return {
         fNode->getAnchorPoint(),
         fNode->getPosition(),
@@ -67,7 +67,7 @@
 }
 
 template <>
-void PropertyHandle<TransformPropertyValue, TransformAdapter>::set(
+void PropertyHandle<TransformPropertyValue, TransformAdapter2D>::set(
         const TransformPropertyValue& t) {
     fNode->setAnchorPoint(t.fAnchorPoint);
     fNode->setPosition(t.fPosition);
diff --git a/modules/skottie/src/SkottieShapeLayer.cpp b/modules/skottie/src/SkottieShapeLayer.cpp
index 197faec..76b5658 100644
--- a/modules/skottie/src/SkottieShapeLayer.cpp
+++ b/modules/skottie/src/SkottieShapeLayer.cpp
@@ -596,7 +596,7 @@
         // of the dangling/uncommitted ones.
         AnimatorScope local_scope;
 
-        if ((shape_matrix = this->attachMatrix(*jtransform, &local_scope, nullptr))) {
+        if ((shape_matrix = this->attachMatrix2D(*jtransform, &local_scope, nullptr))) {
             shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
         }
         shape_wrapper = this->attachOpacity(*jtransform, &local_scope, std::move(shape_wrapper));