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);