| /* |
| * 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 "SkFilterQuality.h" |
| #include "Resources.h" |
| #include <algorithm> |
| |
| 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)); |
| 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, 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; |
| |
| // Get deformed positions if necessary. |
| if (fActorImage->doesAnimationVertexDeform()) { |
| attrPosition = fActorImage->animationDeformedVertices() + i * 2; |
| } |
| |
| // 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); |
| } |