Refactor Nima code

There were two copies of a Nima "player" and this moves them out of
samplecode/ and viewer/ to experimental/ where it is a bit more
accessible (e.g. for WebAssembly).

Bug: skia:
Change-Id: I05419a352f0d13d16b462a374578107513eb1243
Reviewed-on: https://skia-review.googlesource.com/c/166441
Commit-Queue: Kevin Lubick <kjlubick@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/tools/viewer/NIMASlide.cpp b/tools/viewer/NIMASlide.cpp
index c1a3356..bf516a8 100644
--- a/tools/viewer/NIMASlide.cpp
+++ b/tools/viewer/NIMASlide.cpp
@@ -7,10 +7,11 @@
 
 #include "NIMASlide.h"
 
+#include "Resources.h"
 #include "SkAnimTimer.h"
 #include "SkOSPath.h"
-#include "Resources.h"
 #include "imgui.h"
+#include "nima/NimaActor.h"
 
 #include <algorithm>
 #include <cmath>
@@ -27,306 +28,15 @@
     return true;
 }
 
-// A wrapper class that handles rendering of ActorImages (renderable components NIMA Actors).
-class NIMAActorImage {
-public:
-    NIMAActorImage(ActorImage* actorImage, SkImage* texture, SkPaint* paint)
-            : fActorImage(actorImage)
-            , fTexture(texture)
-            , fPaint(paint)
-            , fSkinned(false)
-            , fPositions()
-            , fTexs()
-            , fBoneIdx()
-            , fBoneWgt()
-            , fIndices()
-            , fBones()
-            , fVertices(nullptr)
-            , fRenderFlags(0) {
-        // Update the vertices and bones.
-        this->updateVertices(true);
-        this->updateBones();
-    }
-
-    void render(SkCanvas* canvas, uint32_t renderFlags) {
-        bool dirty = renderFlags != fRenderFlags;
-        fRenderFlags = renderFlags;
-
-        bool useImmediate = renderFlags & kImmediate_RenderFlag;
-        bool useCache = renderFlags & kCache_RenderFlag;
-        bool drawBounds = renderFlags & kBounds_RenderFlag;
-
-        // Don't use the cache if drawing in immediate mode.
-        useCache &= !useImmediate;
-
-        if (fActorImage->doesAnimationVertexDeform() || dirty) {
-            // These are vertices that transform beyond just bone transforms, so they must be
-            // updated every frame.
-            // If the render flags are dirty, reset the vertices object.
-            this->updateVertices(!useCache);
-        }
-
-        // Update the bones.
-        this->updateBones();
-
-        // Deform the bones in immediate mode.
-        sk_sp<SkVertices> vertices = fVertices;
-        if (useImmediate) {
-            vertices = fVertices->applyBones(fBones.data(), fBones.size());
-        }
-
-        // Draw the vertices object.
-        this->drawVerticesObject(vertices.get(), canvas, !useImmediate);
-
-        // Draw the bounds.
-        if (drawBounds && fActorImage->renderOpacity() > 0.0f) {
-            // Get the bounds.
-            SkRect bounds = vertices->bounds();
-
-            // Approximate bounds if not using immediate transforms.
-            if (!useImmediate) {
-                const SkRect originalBounds = fBones[0].mapRect(vertices->bounds());
-                bounds = originalBounds;
-                for (size_t i = 1; i < fBones.size(); i++) {
-                    const SkVertices::Bone& matrix = fBones[i];
-                    bounds.join(matrix.mapRect(originalBounds));
-                }
-            }
-
-            // Draw the bounds.
-            SkPaint paint;
-            paint.setStyle(SkPaint::kStroke_Style);
-            paint.setColor(0xFFFF0000);
-            canvas->drawRect(bounds, paint);
-        }
-    }
-
-    int drawOrder() const { return fActorImage->drawOrder(); }
-
-private:
-    void updateVertices(bool isVolatile) {
-        // Update whether the image is skinned.
-        fSkinned = fActorImage->connectedBoneCount() > 0;
-
-        // 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) {
-            fPositions.clear();
-            fTexs.clear();
-            fBoneIdx.clear();
-            fBoneWgt.clear();
-            fIndices.clear();
-            return;
-        }
-
-        // Split the vertex data.
-        fPositions.resize(vertexCount);
-        fTexs.resize(vertexCount);
-        fIndices.resize(indexCount);
-        if (fSkinned) {
-            fBoneIdx.resize(vertexCount * 4);
-            fBoneWgt.resize(vertexCount * 4);
-        }
-        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;
-
-            // Get deformed positions if necessary.
-            if (fActorImage->doesAnimationVertexDeform()) {
-                attrPosition = fActorImage->animationDeformedVertices() + i * 2;
-            }
-
-            // Set the data.
-            fPositions[i].set(attrPosition[0], attrPosition[1]);
-            fTexs[i].set(attrTex[0] * fTexture->width(), attrTex[1] * fTexture->height());
-            if (fSkinned) {
-                for (uint32_t k = 0; k < 4; k ++) {
-                    fBoneIdx[i][k] = static_cast<uint32_t>(attrBoneIdx[k]);
-                    fBoneWgt[i][k] = attrBoneWgt[k];
-                }
-            }
-        }
-        memcpy(fIndices.data(), indexData, indexCount * sizeof(uint16_t));
-
-        // Update the vertices object.
-        fVertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
-                                         vertexCount,
-                                         fPositions.data(),
-                                         fTexs.data(),
-                                         nullptr,
-                                         fBoneIdx.data(),
-                                         fBoneWgt.data(),
-                                         fIndices.size(),
-                                         fIndices.data(),
-                                         isVolatile);
-    }
-
-    void updateBones() {
-        // NIMA matrices are a collection of 6 floats.
-        constexpr int kNIMAMatrixSize = 6;
-
-        // Set up the matrices for the first time.
-        if (fBones.size() == 0) {
-            int numMatrices = 1;
-            if (fSkinned) {
-                numMatrices = fActorImage->boneInfluenceMatricesLength() / kNIMAMatrixSize;
-            }
-
-            // Initialize all matrices to the identity matrix.
-            fBones.assign(numMatrices, {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }});
-        }
-
-        if (fSkinned) {
-            // Update the matrices.
-            float* matrixData = fActorImage->boneInfluenceMatrices();
-            memcpy(fBones.data(), matrixData, fBones.size() * kNIMAMatrixSize * sizeof(float));
-        }
-
-        // Set the zero matrix to be the world transform.
-        memcpy(fBones.data(),
-               fActorImage->worldTransform().values(),
-               kNIMAMatrixSize * sizeof(float));
-    }
-
-    void drawVerticesObject(SkVertices* vertices, SkCanvas* canvas, bool useBones) const {
-        // 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.
-        if (useBones) {
-            canvas->drawVertices(vertices, fBones.data(), fBones.size(), blendMode, *fPaint);
-        } else {
-            canvas->drawVertices(vertices, blendMode, *fPaint);
-        }
-
-        // Reset the opacity.
-        fPaint->setAlpha(255);
-    }
-
-private:
-    ActorImage* fActorImage;
-    SkImage*    fTexture;
-    SkPaint*    fPaint;
-
-    bool                                 fSkinned;
-    std::vector<SkPoint>                 fPositions;
-    std::vector<SkPoint>                 fTexs;
-    std::vector<SkVertices::BoneIndices> fBoneIdx;
-    std::vector<SkVertices::BoneWeights> fBoneWgt;
-    std::vector<uint16_t>                fIndices;
-
-    std::vector<SkVertices::Bone> fBones;
-    sk_sp<SkVertices>             fVertices;
-
-    uint32_t fRenderFlags;
-};
-
-//////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Represents an Actor, or an animated character, in NIMA.
-class NIMAActor : public Actor {
-public:
-    NIMAActor(const std::string& basePath)
-        : fTexture(nullptr)
-        , fActorImages()
-        , fPaint()
-        , fAnimations() {
-        // Load the NIMA data.
-        std::string nimaPath((basePath + ".nima").c_str());
-        INHERITED::load(nimaPath);
-
-        // Load the image asset.
-        sk_sp<SkData> imageData = SkData::MakeFromFileName((basePath + ".png").c_str());
-        fTexture = SkImage::MakeFromEncoded(imageData);
-
-        // Create the paint.
-        fPaint.setShader(fTexture->makeShader(nullptr));
-        fPaint.setFilterQuality(SkFilterQuality::kLow_SkFilterQuality);
-
-        // Load the image nodes.
-        fActorImages.reserve(m_ImageNodeCount);
-        for (uint32_t i = 0; i < m_ImageNodeCount; i ++) {
-            fActorImages.emplace_back(m_ImageNodes[i], fTexture.get(), &fPaint);
-        }
-
-        // Sort the image nodes.
-        std::sort(fActorImages.begin(), fActorImages.end(), [](auto a, auto b) {
-            return a.drawOrder() < b.drawOrder();
-        });
-
-        // Get the list of animations.
-        fAnimations.reserve(m_AnimationsCount);
-        for (uint32_t i = 0; i < m_AnimationsCount; i ++) {
-            fAnimations.push_back(m_Animations[i].name());
-        }
-    }
-
-    void render(SkCanvas* canvas, uint32_t renderFlags) {
-        // Render the image nodes.
-        for (auto& image : fActorImages) {
-            image.render(canvas, renderFlags);
-        }
-    }
-
-    const std::vector<std::string>& getAnimations() const {
-        return fAnimations;
-    }
-
-private:
-    sk_sp<SkImage>              fTexture;
-    std::vector<NIMAActorImage> fActorImages;
-    SkPaint                     fPaint;
-    std::vector<std::string>    fAnimations;
-
-    typedef Actor INHERITED;
-};
-
 //////////////////////////////////////////////////////////////////////////////////////////////////
 
 NIMASlide::NIMASlide(const SkString& name, const SkString& path)
         : fBasePath()
         , fActor(nullptr)
+        , fAnimationIndex(0)
         , fPlaying(true)
         , fTime(0.0f)
-        , fRenderFlags(0)
-        , fAnimation(nullptr)
-        , fAnimationIndex(0) {
+        , fRenderFlags(0) {
     fName = name;
 
     // Get the path components.
@@ -356,6 +66,7 @@
             canvas->scale(0.5, -0.5);
 
             // Render the actor.
+            fActor->setAnimation(fAnimationIndex);
             fActor->render(canvas, fRenderFlags);
 
             canvas->restore();
@@ -374,18 +85,14 @@
 
 void NIMASlide::unload() {
     // Discard resources.
-    fAnimation = nullptr;
     fActor.reset(nullptr);
 }
 
 bool NIMASlide::animate(const SkAnimTimer& timer) {
     // Apply the animation.
-    if (fAnimation) {
-        if (fPlaying) {
-            fTime = std::fmod(timer.secs(), fAnimation->max());
-        }
-        fAnimation->time(fTime);
-        fAnimation->apply(1.0f);
+    if (fActor) {
+        float time = std::fmod(timer.secs(), fActor->duration());
+        fActor->seek(time);
     }
     return true;
 }
@@ -400,10 +107,10 @@
 
 void NIMASlide::resetActor() {
     // Create the actor.
-    fActor = std::make_unique<NIMAActor>(fBasePath);
+    std::string nimaPath = fBasePath + ".nima";
+    std::string texturePath = fBasePath + ".png";
 
-    // Get the animation.
-    fAnimation = fActor->animationInstance(fActor->getAnimations()[fAnimationIndex]);
+    fActor = std::make_unique<NimaActor>(nimaPath, texturePath);
 }
 
 void NIMASlide::renderGUI() {
@@ -411,7 +118,7 @@
     ImGui::Begin("NIMA");
 
     // List of animations.
-    auto animations = const_cast<std::vector<std::string>&>(fActor->getAnimations());
+    auto animations = const_cast<std::vector<std::string>&>(fActor->getAnimationNames());
     ImGui::PushItemWidth(-1);
     if (ImGui::ListBox("Animations",
                        &fAnimationIndex,
@@ -434,7 +141,7 @@
 
     // Time slider.
     ImGui::PushItemWidth(-1);
-    ImGui::SliderFloat("Time", &fTime, 0.0f, fAnimation->max(), "Time: %.3f");
+    ImGui::SliderFloat("Time", &fTime, 0.0f, fActor->duration(), "Time: %.3f");
 
     // Backend control.
     int useImmediate = SkToBool(fRenderFlags & kImmediate_RenderFlag);