• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
NimaActor(std::string nimaPath,std::string texturePath)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 
NimaActor(sk_sp<SkData> nimaBytes,sk_sp<SkData> textureBytes)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 
init()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 
duration() const75 SkScalar NimaActor::duration() const {
76     if (fAnimationInstance) {
77         return fAnimationInstance->duration();
78     }
79     return 0.0f;
80 }
81 
setAnimation(uint8_t index)82 void NimaActor::setAnimation(uint8_t index) {
83     if (index < fAnimationNames.size()) {
84         fAnimationIndex = index;
85         fAnimationInstance = this->animationInstance(fAnimationNames[fAnimationIndex]);
86     }
87 }
88 
setAnimation(std::string name)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 
render(SkCanvas * canvas,uint32_t renderFlags)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 
seek(SkScalar t)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 
NimaActorImage(nima::ActorImage * actorImage,SkImage * texture,SkPaint * paint)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 
render(SkCanvas * canvas,uint32_t renderFlags)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 
updateVertices(bool isVolatile)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 
updateBones()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 
drawVerticesObject(SkVertices * vertices,SkCanvas * canvas,bool useBones) const285 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 }
324