Kevin Lubick | d969932 | 2018-10-30 15:08:53 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2018 Google Inc. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license that can be |
| 5 | * found in the LICENSE file. |
| 6 | */ |
| 7 | |
| 8 | #include "NimaActor.h" |
| 9 | |
| 10 | #include "SkData.h" |
| 11 | #include "SkFilterQuality.h" |
| 12 | #include "SkImage.h" |
| 13 | #include "SkPaint.h" |
| 14 | #include "SkString.h" |
| 15 | #include "SkVertices.h" |
| 16 | |
| 17 | #include <algorithm> |
| 18 | #include <cmath> |
| 19 | |
| 20 | NimaActor::NimaActor(std::string nimaPath, std::string texturePath) |
| 21 | : fTexture(nullptr) |
| 22 | , fActorImages() |
| 23 | , fPaint(nullptr) |
| 24 | , fAnimationNames() |
| 25 | , fAnimationInstance(nullptr) { |
| 26 | // Load the NIMA data. |
| 27 | INHERITED::load(nimaPath); |
| 28 | |
| 29 | // Load the image asset. |
| 30 | fTexture = SkImage::MakeFromEncoded(SkData::MakeFromFileName(texturePath.c_str())); |
| 31 | |
| 32 | this->init(); |
| 33 | } |
| 34 | |
| 35 | NimaActor::NimaActor(sk_sp<SkData> nimaBytes, sk_sp<SkData> textureBytes) |
| 36 | : fTexture(nullptr) |
| 37 | , fActorImages() |
| 38 | , fPaint(nullptr) |
| 39 | , fAnimationNames() |
| 40 | , fAnimationInstance(nullptr) { |
| 41 | // Load the NIMA data. |
| 42 | INHERITED::load(const_cast<uint8_t*>(nimaBytes->bytes()), nimaBytes->size()); |
| 43 | |
| 44 | // Load the image asset. |
| 45 | fTexture = SkImage::MakeFromEncoded(textureBytes); |
| 46 | |
| 47 | this->init(); |
| 48 | } |
| 49 | |
| 50 | void NimaActor::init() { |
| 51 | // Create the paint. |
| 52 | fPaint = std::make_unique<SkPaint>(); |
| 53 | fPaint->setShader(fTexture->makeShader(nullptr)); |
| 54 | fPaint->setFilterQuality(SkFilterQuality::kLow_SkFilterQuality); |
| 55 | |
| 56 | // Load the image nodes. |
| 57 | fActorImages.reserve(m_ImageNodeCount); |
| 58 | for (uint32_t i = 0; i < m_ImageNodeCount; i ++) { |
| 59 | fActorImages.emplace_back(m_ImageNodes[i], fTexture.get(), fPaint.get()); |
| 60 | } |
| 61 | |
| 62 | // Sort the image nodes. |
| 63 | std::sort(fActorImages.begin(), fActorImages.end(), [](auto a, auto b) { |
| 64 | return a.drawOrder() < b.drawOrder(); |
| 65 | }); |
| 66 | |
| 67 | // Get the list of animations. |
| 68 | fAnimationNames.reserve(m_AnimationsCount); |
| 69 | for (uint32_t i = 0; i < m_AnimationsCount; i++) { |
| 70 | fAnimationNames.push_back(m_Animations[i].name()); |
| 71 | } |
| 72 | this->setAnimation(0); |
| 73 | } |
| 74 | |
| 75 | SkScalar NimaActor::duration() const { |
| 76 | if (fAnimationInstance) { |
| 77 | return fAnimationInstance->duration(); |
| 78 | } |
| 79 | return 0.0f; |
| 80 | } |
| 81 | |
| 82 | void NimaActor::setAnimation(uint8_t index) { |
| 83 | if (index < fAnimationNames.size()) { |
| 84 | fAnimationIndex = index; |
| 85 | fAnimationInstance = this->animationInstance(fAnimationNames[fAnimationIndex]); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | void NimaActor::setAnimation(std::string name) { |
| 90 | for (size_t i = 0; i < fAnimationNames.size(); i++) |
| 91 | { |
| 92 | std::string aName = fAnimationNames[i]; |
| 93 | if (aName == name) |
| 94 | { |
| 95 | setAnimation(i); |
| 96 | return; |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | void NimaActor::render(SkCanvas* canvas, uint32_t renderFlags) { |
| 102 | // Render the image nodes. |
| 103 | for (auto& image : fActorImages) { |
| 104 | image.render(canvas, renderFlags); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | void NimaActor::seek(SkScalar t) { |
| 109 | // Apply the animation. |
| 110 | if (fAnimationInstance) { |
| 111 | t = std::fmod(t, fAnimationInstance->max()); |
| 112 | fAnimationInstance->time(t); |
| 113 | fAnimationInstance->apply(1.0f); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | // =================================================================================== |
| 118 | |
| 119 | NimaActorImage::NimaActorImage(nima::ActorImage* actorImage, SkImage* texture, SkPaint* paint) |
| 120 | : fActorImage(actorImage) |
| 121 | , fTexture(texture) |
| 122 | , fPaint(paint) |
| 123 | , fSkinned(false) |
| 124 | , fPositions() |
| 125 | , fTexs() |
| 126 | , fBoneIdx() |
| 127 | , fBoneWgt() |
| 128 | , fIndices() |
| 129 | , fBones() |
| 130 | , fVertices(nullptr) |
| 131 | , fRenderFlags(0) { |
| 132 | // Update the vertices and bones. |
| 133 | this->updateVertices(true); |
| 134 | this->updateBones(); |
| 135 | } |
| 136 | |
| 137 | void NimaActorImage::render(SkCanvas* canvas, uint32_t renderFlags) { |
| 138 | bool dirty = renderFlags != fRenderFlags; |
| 139 | fRenderFlags = renderFlags; |
| 140 | |
| 141 | bool useImmediate = renderFlags & kImmediate_RenderFlag; |
| 142 | bool useCache = renderFlags & kCache_RenderFlag; |
| 143 | bool drawBounds = renderFlags & kBounds_RenderFlag; |
| 144 | |
| 145 | // Don't use the cache if drawing in immediate mode. |
| 146 | useCache &= !useImmediate; |
| 147 | |
| 148 | if (fActorImage->doesAnimationVertexDeform() || dirty) { |
| 149 | // These are vertices that transform beyond just bone transforms, so they must be |
| 150 | // updated every frame. |
| 151 | // If the render flags are dirty, reset the vertices object. |
| 152 | this->updateVertices(!useCache); |
| 153 | } |
| 154 | |
| 155 | // Update the bones. |
| 156 | this->updateBones(); |
| 157 | |
| 158 | // Deform the bones in immediate mode. |
| 159 | sk_sp<SkVertices> vertices = fVertices; |
| 160 | if (useImmediate) { |
| 161 | vertices = fVertices->applyBones(fBones.data(), fBones.size()); |
| 162 | } |
| 163 | |
| 164 | // Draw the vertices object. |
| 165 | this->drawVerticesObject(vertices.get(), canvas, !useImmediate); |
| 166 | |
| 167 | // Draw the bounds. |
| 168 | if (drawBounds && fActorImage->renderOpacity() > 0.0f) { |
| 169 | // Get the bounds. |
| 170 | SkRect bounds = vertices->bounds(); |
| 171 | |
| 172 | // Approximate bounds if not using immediate transforms. |
| 173 | if (!useImmediate) { |
| 174 | const SkRect originalBounds = fBones[0].mapRect(vertices->bounds()); |
| 175 | bounds = originalBounds; |
| 176 | for (size_t i = 1; i < fBones.size(); i++) { |
| 177 | const SkVertices::Bone& matrix = fBones[i]; |
| 178 | bounds.join(matrix.mapRect(originalBounds)); |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | // Draw the bounds. |
| 183 | SkPaint paint; |
| 184 | paint.setStyle(SkPaint::kStroke_Style); |
| 185 | paint.setColor(0xFFFF0000); |
| 186 | canvas->drawRect(bounds, paint); |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | void NimaActorImage::updateVertices(bool isVolatile) { |
| 191 | // Update whether the image is skinned. |
| 192 | fSkinned = fActorImage->connectedBoneCount() > 0; |
| 193 | |
| 194 | // Retrieve data from the image. |
| 195 | uint32_t vertexCount = fActorImage->vertexCount(); |
| 196 | uint32_t vertexStride = fActorImage->vertexStride(); |
| 197 | float* vertexData = fActorImage->vertices(); |
| 198 | uint32_t indexCount = fActorImage->triangleCount() * 3; |
| 199 | uint16_t* indexData = fActorImage->triangles(); |
| 200 | |
| 201 | // Don't render if not visible. |
| 202 | if (!vertexCount || fActorImage->textureIndex() < 0) { |
| 203 | fPositions.clear(); |
| 204 | fTexs.clear(); |
| 205 | fBoneIdx.clear(); |
| 206 | fBoneWgt.clear(); |
| 207 | fIndices.clear(); |
| 208 | return; |
| 209 | } |
| 210 | |
| 211 | // Split the vertex data. |
| 212 | fPositions.resize(vertexCount); |
| 213 | fTexs.resize(vertexCount); |
| 214 | fIndices.resize(indexCount); |
| 215 | if (fSkinned) { |
| 216 | fBoneIdx.resize(vertexCount * 4); |
| 217 | fBoneWgt.resize(vertexCount * 4); |
| 218 | } |
| 219 | for (uint32_t i = 0; i < vertexCount; i ++) { |
| 220 | uint32_t j = i * vertexStride; |
| 221 | |
| 222 | // Get the attributes. |
| 223 | float* attrPosition = vertexData + j; |
| 224 | float* attrTex = vertexData + j + 2; |
| 225 | float* attrBoneIdx = vertexData + j + 4; |
| 226 | float* attrBoneWgt = vertexData + j + 8; |
| 227 | |
| 228 | // Get deformed positions if necessary. |
| 229 | if (fActorImage->doesAnimationVertexDeform()) { |
| 230 | attrPosition = fActorImage->animationDeformedVertices() + i * 2; |
| 231 | } |
| 232 | |
| 233 | // Set the data. |
| 234 | fPositions[i].set(attrPosition[0], attrPosition[1]); |
| 235 | fTexs[i].set(attrTex[0] * fTexture->width(), attrTex[1] * fTexture->height()); |
| 236 | if (fSkinned) { |
| 237 | for (uint32_t k = 0; k < 4; k ++) { |
| 238 | fBoneIdx[i][k] = static_cast<uint32_t>(attrBoneIdx[k]); |
| 239 | fBoneWgt[i][k] = attrBoneWgt[k]; |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | memcpy(fIndices.data(), indexData, indexCount * sizeof(uint16_t)); |
| 244 | |
| 245 | // Update the vertices object. |
| 246 | fVertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode, |
| 247 | vertexCount, |
| 248 | fPositions.data(), |
| 249 | fTexs.data(), |
| 250 | nullptr, |
| 251 | fBoneIdx.data(), |
| 252 | fBoneWgt.data(), |
| 253 | fIndices.size(), |
| 254 | fIndices.data(), |
| 255 | isVolatile); |
| 256 | } |
| 257 | |
| 258 | void NimaActorImage::updateBones() { |
| 259 | // NIMA matrices are a collection of 6 floats. |
| 260 | constexpr int kNIMAMatrixSize = 6; |
| 261 | |
| 262 | // Set up the matrices for the first time. |
| 263 | if (fBones.size() == 0) { |
| 264 | int numMatrices = 1; |
| 265 | if (fSkinned) { |
| 266 | numMatrices = fActorImage->boneInfluenceMatricesLength() / kNIMAMatrixSize; |
| 267 | } |
| 268 | |
| 269 | // Initialize all matrices to the identity matrix. |
| 270 | fBones.assign(numMatrices, {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }}); |
| 271 | } |
| 272 | |
| 273 | if (fSkinned) { |
| 274 | // Update the matrices. |
| 275 | float* matrixData = fActorImage->boneInfluenceMatrices(); |
| 276 | memcpy(fBones.data(), matrixData, fBones.size() * kNIMAMatrixSize * sizeof(float)); |
| 277 | } |
| 278 | |
| 279 | // Set the zero matrix to be the world transform. |
| 280 | memcpy(fBones.data(), |
| 281 | fActorImage->worldTransform().values(), |
| 282 | kNIMAMatrixSize * sizeof(float)); |
| 283 | } |
| 284 | |
| 285 | void NimaActorImage::drawVerticesObject(SkVertices* vertices, SkCanvas* canvas, bool useBones) const { |
| 286 | // Determine the blend mode. |
| 287 | SkBlendMode blendMode; |
| 288 | switch (fActorImage->blendMode()) { |
| 289 | case nima::BlendMode::Off: { |
| 290 | blendMode = SkBlendMode::kSrc; |
| 291 | break; |
| 292 | } |
| 293 | case nima::BlendMode::Normal: { |
| 294 | blendMode = SkBlendMode::kSrcOver; |
| 295 | break; |
| 296 | } |
| 297 | case nima::BlendMode::Additive: { |
| 298 | blendMode = SkBlendMode::kPlus; |
| 299 | break; |
| 300 | } |
| 301 | case nima::BlendMode::Multiply: { |
| 302 | blendMode = SkBlendMode::kMultiply; |
| 303 | break; |
| 304 | } |
| 305 | case nima::BlendMode::Screen: { |
| 306 | blendMode = SkBlendMode::kScreen; |
| 307 | break; |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | // Set the opacity. |
| 312 | fPaint->setAlpha(static_cast<U8CPU>(fActorImage->renderOpacity() * 255)); |
| 313 | |
| 314 | // Draw the vertices. |
| 315 | if (useBones) { |
| 316 | canvas->drawVertices(vertices, fBones.data(), fBones.size(), blendMode, *fPaint); |
| 317 | } else { |
| 318 | canvas->drawVertices(vertices, blendMode, *fPaint); |
| 319 | } |
| 320 | |
| 321 | // Reset the opacity. |
| 322 | fPaint->setAlpha(255); |
| 323 | } |