added NIMA sample to showcase animations

improved third_party template to include headers as system headers for non-Windows machines

Bug: skia:
Change-Id: Id2fa74fc31b49f9b07cc83e7f60477c7ab4f8d83
Reviewed-on: https://skia-review.googlesource.com/135450
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 3f58f44..a433b92 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1547,6 +1547,7 @@
         ":xml",
         "modules/sksg:samples",
         "modules/skshaper",
+        "//third_party/Nima-Cpp",
       ]
 
       if (skia_use_lua) {
@@ -1576,6 +1577,7 @@
         ":tool_utils",
         "modules/skottie",
         "modules/sksg",
+        "//third_party/Nima-Cpp",
         "//third_party/jsoncpp",
         "//third_party/libpng",
       ]
@@ -1999,6 +2001,7 @@
       ":views",
       "modules/skottie",
       "modules/sksg",
+      "//third_party/Nima-Cpp",
       "//third_party/imgui",
     ]
   }
diff --git a/DEPS b/DEPS
index 0eeac33..ebc2696 100644
--- a/DEPS
+++ b/DEPS
@@ -28,6 +28,8 @@
   "third_party/externals/swiftshader"     : "https://swiftshader.googlesource.com/SwiftShader@1fa20678010892b3b531e7449f25079868dfba45",
   #"third_party/externals/v8"              : "https://chromium.googlesource.com/v8/v8.git@5f1ae66d5634e43563b2d25ea652dfb94c31a3b4",
   "third_party/externals/zlib"            : "https://chromium.googlesource.com/chromium/src/third_party/zlib@e7afdfe128e01ca480a28f757b571957befdd962",
+  "third_party/externals/Nima-Cpp"      : "https://github.com/2d-inc/Nima-Cpp.git@4bd02269d7d1d2e650950411325eafa15defb084",
+  "third_party/externals/Nima-Math-Cpp" : "https://github.com/2d-inc/Nima-Math-Cpp.git@e0c12772093fa8860f55358274515b86885f0108",
 }
 
 recursedeps = [ "common" ]
diff --git a/gn/samples.gni b/gn/samples.gni
index 1a4002f..ce73ab8 100644
--- a/gn/samples.gni
+++ b/gn/samples.gni
@@ -8,6 +8,8 @@
 
 samples_sources = [
   "$_samplecode/ClockFaceView.cpp",
+  "$_samplecode/Nima.cpp",
+  "$_samplecode/Nima.h",
   "$_samplecode/PerlinPatch.cpp",
   "$_samplecode/Sample2PtRadial.cpp",
   "$_samplecode/SampleAAClip.cpp",
@@ -64,6 +66,7 @@
   "$_samplecode/SampleManyRects.cpp",
   "$_samplecode/SampleMeasure.cpp",
   "$_samplecode/SampleMegaStroke.cpp",
+  "$_samplecode/SampleNima.cpp",
   "$_samplecode/SamplePatch.cpp",
   "$_samplecode/SamplePath.cpp",
   "$_samplecode/SamplePathText.cpp",
@@ -103,3 +106,11 @@
   "$_samplecode/SampleXfermodesBlur.cpp",
   "$_samplecode/vertexdump.cpp",
 ]
+
+if (is_win && is_clang) {
+  samples_sources -= [
+    "$_samplecode/Nima.cpp",
+    "$_samplecode/Nima.h",
+    "$_samplecode/SampleNima.cpp",
+  ]
+}
diff --git a/resources/nima/Robot.nima b/resources/nima/Robot.nima
new file mode 100644
index 0000000..4e56985
--- /dev/null
+++ b/resources/nima/Robot.nima
Binary files differ
diff --git a/resources/nima/Robot.png b/resources/nima/Robot.png
new file mode 100644
index 0000000..595616a
--- /dev/null
+++ b/resources/nima/Robot.png
Binary files differ
diff --git a/samplecode/Nima.cpp b/samplecode/Nima.cpp
new file mode 100644
index 0000000..4ac7d86
--- /dev/null
+++ b/samplecode/Nima.cpp
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Nima.h"
+
+#include "SkString.h"
+#include "SkVertices.h"
+#include "SkPaint.h"
+#include "Resources.h"
+#include <algorithm>
+
+#include <iostream>
+
+using namespace nima;
+
+SampleActor::SampleActor(std::string baseName)
+    : fTexture(nullptr)
+    , fActorImages()
+    , fPaint(nullptr) {
+    // Load the NIMA data.
+    SkString nimaSkPath = GetResourcePath(("nima/" + baseName + ".nima").c_str());
+    std::string nimaPath(nimaSkPath.c_str());
+    INHERITED::load(nimaPath);
+
+    // Load the image asset.
+    fTexture = GetResourceAsImage(("nima/" + baseName + ".png").c_str());
+
+    // Create the paint.
+    fPaint = std::make_unique<SkPaint>();
+    fPaint->setShader(fTexture->makeShader(nullptr));
+
+    // Load the image nodes.
+    fActorImages.reserve(m_ImageNodeCount);
+    for (uint32_t i = 0; i < m_ImageNodeCount; i ++) {
+        fActorImages.emplace_back(m_ImageNodes[i], fTexture, fPaint.get());
+    }
+
+    // Sort the image nodes.
+    std::sort(fActorImages.begin(), fActorImages.end(), [](auto a, auto b) {
+        return a.drawOrder() < b.drawOrder();
+    });
+}
+
+SampleActor::~SampleActor() {
+}
+
+void SampleActor::render(SkCanvas* canvas) const {
+    // Render the image nodes.
+    for (auto image : fActorImages) {
+        image.render(this, canvas);
+    }
+}
+
+SampleActorImage::SampleActorImage(ActorImage* actorImage, sk_sp<SkImage> texture, SkPaint* paint)
+    : fActorImage(actorImage)
+    , fTexture(texture)
+    , fPaint(paint) {
+}
+
+SampleActorImage::~SampleActorImage() {
+}
+
+void SampleActorImage::render(const SampleActor* actor, SkCanvas* canvas) const {
+    // Retrieve data from the image.
+    uint32_t  vertexCount  = fActorImage->vertexCount();
+    uint32_t  vertexStride = fActorImage->vertexStride();
+    float*    vertexData   = fActorImage->vertices();
+    uint32_t  indexCount   = fActorImage->triangleCount() * 3;
+    uint16_t* indexData    = fActorImage->triangles();
+
+    // Don't render if not visible.
+    if (!vertexCount || fActorImage->textureIndex() < 0) {
+        return;
+    }
+
+    // Split the vertex data.
+    std::vector<SkPoint>  positions(vertexCount);
+    std::vector<SkPoint>  texs(vertexCount);
+    for (uint32_t i = 0; i < vertexCount; i ++) {
+        uint32_t j = i * vertexStride;
+
+        // Get the attributes.
+        float* attrPosition = vertexData + j;
+        float* attrTex      = vertexData + j + 2;
+        float* attrBoneIdx  = vertexData + j + 4;
+        float* attrBoneWgt  = vertexData + j + 8;
+
+        // Deform the position.
+        Vec2D position(attrPosition[0], attrPosition[1]);
+        if (fActorImage->connectedBoneCount() > 0) {
+            position = deform(position, attrBoneIdx, attrBoneWgt);
+        } else {
+            position = deform(position, nullptr, nullptr);
+        }
+
+        // Set the data.
+        positions[i].set(position[0], position[1]);
+        texs[i].set(attrTex[0] * fTexture->width(), attrTex[1] * fTexture->height());
+    }
+
+    // Create vertices.
+    sk_sp<SkVertices> vertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
+                                                      vertexCount,
+                                                      positions.data(),
+                                                      texs.data(),
+                                                      nullptr,
+                                                      indexCount,
+                                                      indexData);
+
+    // Determine the blend mode.
+    SkBlendMode blendMode;
+    switch (fActorImage->blendMode()) {
+        case BlendMode::Off: {
+            blendMode = SkBlendMode::kSrc;
+            break;
+        }
+        case BlendMode::Normal: {
+            blendMode = SkBlendMode::kSrcOver;
+            break;
+        }
+        case BlendMode::Additive: {
+            blendMode = SkBlendMode::kPlus;
+            break;
+        }
+        case BlendMode::Multiply: {
+            blendMode = SkBlendMode::kMultiply;
+            break;
+        }
+        case BlendMode::Screen: {
+            blendMode = SkBlendMode::kScreen;
+            break;
+        }
+    }
+
+    // Set the opacity.
+    fPaint->setAlpha(static_cast<U8CPU>(fActorImage->renderOpacity() * 255));
+
+    // Draw the vertices.
+    canvas->drawVertices(vertices, blendMode, *fPaint);
+
+    // Reset the opacity.
+    fPaint->setAlpha(255);
+}
+
+Vec2D SampleActorImage::deform(const Vec2D& position, float* boneIdx, float* boneWgt) const {
+    float px = position[0], py = position[1];
+    float px2 = px, py2 = py;
+    float influence[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
+    // Apply the world transform.
+    Mat2D worldTransform = fActorImage->worldTransform();
+    px2 = worldTransform[0] * px + worldTransform[2] * py + worldTransform[4];
+    py2 = worldTransform[1] * px + worldTransform[3] * py + worldTransform[5];
+
+    // Apply deformations based on bone offsets.
+    if (boneIdx && boneWgt) {
+        float* matrices = fActorImage->boneInfluenceMatrices();
+
+        for (uint32_t i = 0; i < 4; i ++) {
+            int index = static_cast<int>(boneIdx[i]);
+            float weight = boneWgt[i];
+            for (int j = 0; j < 6; j ++) {
+                influence[j] += matrices[index * 6 + j] * weight;
+            }
+        }
+
+        px = influence[0] * px2 + influence[2] * py2 + influence[4];
+        py = influence[1] * px2 + influence[3] * py2 + influence[5];
+    } else {
+        px = px2;
+        py = py2;
+    }
+
+    // Return the transformed position.
+    return Vec2D(px, py);
+}
diff --git a/samplecode/Nima.h b/samplecode/Nima.h
new file mode 100644
index 0000000..0017fcf
--- /dev/null
+++ b/samplecode/Nima.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef Nima_DEFINED
+#define Nima_DEFINED
+
+#include <nima/Actor.hpp>
+#include <nima/ActorImage.hpp>
+#include <nima/Vec2D.hpp>
+
+#include "SkCanvas.h"
+#include "SkImage.h"
+
+class SampleActor;
+class SampleActorImage;
+
+class SampleActor : public nima::Actor {
+public:
+    SampleActor(std::string baseName);
+    ~SampleActor();
+
+    void render(SkCanvas* canvas) const;
+
+private:
+    sk_sp<SkImage>                fTexture;
+    std::vector<SampleActorImage> fActorImages;
+    std::unique_ptr<SkPaint>      fPaint;
+
+    typedef nima::Actor INHERITED;
+};
+
+class SampleActorImage {
+public:
+    SampleActorImage(nima::ActorImage* actorImage, sk_sp<SkImage> texture, SkPaint* paint);
+    ~SampleActorImage();
+
+    void render(const SampleActor* actor, SkCanvas* canvas) const;
+
+    int drawOrder() const { return fActorImage->drawOrder(); }
+
+private:
+    nima::Vec2D deform(const nima::Vec2D& position, float* boneIdx, float* boneWgt) const;
+
+private:
+    nima::ActorImage* fActorImage;
+    sk_sp<SkImage>    fTexture;
+    SkPaint*          fPaint;
+};
+
+#endif
diff --git a/samplecode/SampleNima.cpp b/samplecode/SampleNima.cpp
new file mode 100644
index 0000000..ec7813b
--- /dev/null
+++ b/samplecode/SampleNima.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SampleCode.h"
+#include "Nima.h"
+#include "SkAnimTimer.h"
+#include "SkView.h"
+#include <nima/Animation/ActorAnimation.hpp>
+#include <cmath>
+
+using namespace nima;
+
+class NimaView : public SampleView {
+public:
+    NimaView()
+        : fActor(nullptr) {
+    }
+
+protected:
+    // overrides from SkEventSink
+    virtual bool onQuery(SkEvent* evt) override {
+        if (SampleCode::TitleQ(*evt)) {
+            SampleCode::TitleR(evt, "Nima");
+            return true;
+        }
+        return this->INHERITED::onQuery(evt);
+    }
+
+    void onOnceBeforeDraw() override {
+        // Create the actor.
+        fActor = std::make_unique<SampleActor>("Robot");
+
+        // Get the animation.
+        fAnimation = fActor->animation("jump");
+    }
+
+    void onDrawContent(SkCanvas* canvas) override {
+        canvas->save();
+
+        canvas->translate(500, 500);
+        canvas->scale(1, -1);
+
+        // Render the actor.
+        fActor->render(canvas);
+
+        canvas->restore();
+    }
+
+    bool onAnimate(const SkAnimTimer& timer) override {
+        // Apply the animation.
+        if (fAnimation) {
+            float time = std::fmod(timer.secs(), fAnimation->duration());
+            fAnimation->apply(time, fActor.get(), 1.0f);
+        }
+        return true;
+    }
+
+private:
+    std::unique_ptr<SampleActor> fActor;
+    ActorAnimation*              fAnimation = nullptr;
+
+    typedef SampleView INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static SkView* MyFactory() { return new NimaView; }
+static SkViewRegister reg(MyFactory);
diff --git a/third_party/Nima-Cpp/BUILD.gn b/third_party/Nima-Cpp/BUILD.gn
new file mode 100644
index 0000000..f3dc8b2
--- /dev/null
+++ b/third_party/Nima-Cpp/BUILD.gn
@@ -0,0 +1,101 @@
+# Copyright 2018 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("../third_party.gni")
+
+copy("copy-nima-cpp") {
+  sources = [
+    "../externals/Nima-Cpp/Source",
+  ]
+  outputs = [
+    "$target_gen_dir/Nima-Cpp/nima",
+  ]
+}
+
+copy("copy-nima-math-cpp") {
+  sources = [
+    "../externals/Nima-Math-Cpp/Source",
+  ]
+  outputs = [
+    "$target_gen_dir/Nima-Math-Cpp/nima",
+  ]
+}
+
+third_party("Nima-Cpp") {
+  deps = [
+    ":copy-nima-cpp",
+    ":copy-nima-math-cpp",
+  ]
+
+  public_include_dirs = [
+    "$target_gen_dir/Nima-Cpp",
+    "$target_gen_dir/Nima-Math-Cpp",
+  ]
+
+  configs -= [
+    "//gn:no_exceptions",
+    "//gn:no_rtti",
+  ]
+
+  sources = [
+    "../externals/Nima-Cpp/Source/Actor.cpp",
+    "../externals/Nima-Cpp/Source/ActorBone.cpp",
+    "../externals/Nima-Cpp/Source/ActorCollider.cpp",
+    "../externals/Nima-Cpp/Source/ActorComponent.cpp",
+    "../externals/Nima-Cpp/Source/ActorEvent.cpp",
+    "../externals/Nima-Cpp/Source/ActorIKTarget.cpp",
+    "../externals/Nima-Cpp/Source/ActorImage.cpp",
+    "../externals/Nima-Cpp/Source/ActorInstance.cpp",
+    "../externals/Nima-Cpp/Source/ActorNode.cpp",
+    "../externals/Nima-Cpp/Source/ActorNodeSolo.cpp",
+    "../externals/Nima-Cpp/Source/ActorRenderNode.cpp",
+    "../externals/Nima-Cpp/Source/ActorRootBone.cpp",
+    "../externals/Nima-Cpp/Source/ActorStaticMesh.cpp",
+    "../externals/Nima-Cpp/Source/Animation/ActorAnimation.cpp",
+    "../externals/Nima-Cpp/Source/Animation/ActorAnimationInstance.cpp",
+    "../externals/Nima-Cpp/Source/Animation/ComponentAnimation.cpp",
+    "../externals/Nima-Cpp/Source/Animation/Interpolators/CubicSolver.cpp",
+    "../externals/Nima-Cpp/Source/Animation/Interpolators/ValueTimeCurveInterpolator.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrame.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameActiveChild.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameCustomProperty.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameDrawOrder.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameIKStrength.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameIsCollisionEnabled.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameLength.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameNumeric.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameOpacity.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFramePosX.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFramePosY.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameRotation.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameScaleX.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameScaleY.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameSequence.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameTrigger.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameVertexDeform.cpp",
+    "../externals/Nima-Cpp/Source/Animation/KeyFrames/KeyFrameWithInterpolation.cpp",
+    "../externals/Nima-Cpp/Source/Animation/PropertyAnimation.cpp",
+    "../externals/Nima-Cpp/Source/BinaryReader.cpp",
+    "../externals/Nima-Cpp/Source/BlockReader.cpp",
+    "../externals/Nima-Cpp/Source/CustomProperty.cpp",
+    "../externals/Nima-Cpp/Source/NestedActorAsset.cpp",
+    "../externals/Nima-Cpp/Source/NestedActorNode.cpp",
+    "../externals/Nima-Math-Cpp/Source/Mat2D.cpp",
+    "../externals/Nima-Math-Cpp/Source/Vec2D.cpp",
+  ]
+
+  testonly = true
+
+  cflags_cc = []
+  if (is_win) {
+    defines = [ "_USE_MATH_DEFINES" ]
+    cflags_cc += [
+      "/FI",
+      "algorithm",
+    ]
+  }
+
+  enabled = !is_win || !is_clang
+}
diff --git a/third_party/third_party.gni b/third_party/third_party.gni
index 7aaf635..384eeed 100644
--- a/third_party/third_party.gni
+++ b/third_party/third_party.gni
@@ -4,21 +4,38 @@
 # found in the LICENSE file.
 
 template("third_party") {
+  enabled = !defined(invoker.enabled) || invoker.enabled
   config(target_name + "_public") {
-    if (defined(invoker.public_defines)) {
-      defines = invoker.public_defines
+    if (enabled) {
+      cflags = []
+      if (defined(invoker.public_defines)) {
+        defines = invoker.public_defines
+      }
+      if (is_win) {
+        include_dirs = invoker.public_include_dirs
+      } else {
+        foreach(dir, invoker.public_include_dirs) {
+          cflags += [
+            "-isystem",
+            rebase_path(dir),
+          ]
+        }
+      }
+    } else {
+      not_needed(invoker, "*")
     }
-    include_dirs = invoker.public_include_dirs
   }
   source_set(target_name) {
-    forward_variables_from(invoker, "*", [ "public_include_dirs" ])
-    public_configs = [ ":" + target_name + "_public" ]
+    if (enabled) {
+      forward_variables_from(invoker, "*", [ "public_include_dirs" ])
+      public_configs = [ ":" + target_name + "_public" ]
 
-    # Warnings are just noise if we're not maintaining the code.
-    if (is_win) {
-      cflags = [ "/w" ]
-    } else {
-      cflags = [ "-w" ]
+      # Warnings are just noise if we're not maintaining the code.
+      if (is_win) {
+        cflags = [ "/w" ]
+      } else {
+        cflags = [ "-w" ]
+      }
     }
   }
 }