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