blob: 4d094c4aac604c00d6e276d343a7d5dd551b302f [file] [log] [blame]
Ruiqi Maof5101492018-06-29 14:32:21 -04001/*
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 "NIMASlide.h"
9
10#include "SkAnimTimer.h"
11#include "SkOSPath.h"
12#include "Resources.h"
13#include "imgui.h"
14
15#include <algorithm>
16#include <cmath>
17
18using namespace sk_app;
19using namespace nima;
20
Ruiqi Maof5101492018-06-29 14:32:21 -040021// ImGui expects an array of const char* when displaying a ListBox. This function is for an
22// overload of ImGui::ListBox that takes a getter so that ListBox works with
23// std::vector<std::string>.
24static bool vector_getter(void* v, int index, const char** out) {
25 auto vector = reinterpret_cast<std::vector<std::string>*>(v);
26 *out = vector->at(index).c_str();
27 return true;
28}
29
30// A wrapper class that handles rendering of ActorImages (renderable components NIMA Actors).
31class NIMAActorImage {
32public:
33 NIMAActorImage(ActorImage* actorImage, SkImage* texture, SkPaint* paint)
34 : fActorImage(actorImage)
35 , fTexture(texture)
36 , fPaint(paint)
37 , fSkinned(false)
38 , fPositions()
39 , fTexs()
40 , fBoneIdx()
41 , fBoneWgt()
42 , fIndices()
43 , fBones()
44 , fVertices(nullptr)
Ruiqi Mao46656e22018-07-20 14:18:50 -040045 , fRenderFlags(0) {
Ruiqi Maof5101492018-06-29 14:32:21 -040046 // Update the vertices and bones.
Ruiqi Maoc97a3392018-08-15 10:44:19 -040047 this->updateVertices(true);
Ruiqi Maof5101492018-06-29 14:32:21 -040048 this->updateBones();
Ruiqi Maof5101492018-06-29 14:32:21 -040049 }
50
Ruiqi Mao46656e22018-07-20 14:18:50 -040051 void render(SkCanvas* canvas, uint32_t renderFlags) {
52 bool dirty = renderFlags != fRenderFlags;
53 fRenderFlags = renderFlags;
Ruiqi Maof5101492018-06-29 14:32:21 -040054
Ruiqi Mao46656e22018-07-20 14:18:50 -040055 bool useImmediate = renderFlags & kImmediate_RenderFlag;
56 bool useCache = renderFlags & kCache_RenderFlag;
57 bool drawBounds = renderFlags & kBounds_RenderFlag;
Ruiqi Maof5101492018-06-29 14:32:21 -040058
Ruiqi Maoc97a3392018-08-15 10:44:19 -040059 // Don't use the cache if drawing in immediate mode.
60 useCache &= !useImmediate;
61
62 if (fActorImage->doesAnimationVertexDeform() || dirty) {
63 // These are vertices that transform beyond just bone transforms, so they must be
64 // updated every frame.
65 // If the render flags are dirty, reset the vertices object.
66 this->updateVertices(!useCache);
67 }
68
69 // Update the bones.
70 this->updateBones();
71
72 // Deform the bones in immediate mode.
73 sk_sp<SkVertices> vertices = fVertices;
Ruiqi Mao46656e22018-07-20 14:18:50 -040074 if (useImmediate) {
Ruiqi Maoc97a3392018-08-15 10:44:19 -040075 vertices = fVertices->applyBones(fBones.data(), fBones.size());
Ruiqi Mao46656e22018-07-20 14:18:50 -040076 }
Ruiqi Maof5101492018-06-29 14:32:21 -040077
78 // Draw the vertices object.
Ruiqi Maoc97a3392018-08-15 10:44:19 -040079 this->drawVerticesObject(vertices.get(), canvas, !useImmediate);
Ruiqi Maof5101492018-06-29 14:32:21 -040080
Ruiqi Maoc97a3392018-08-15 10:44:19 -040081 // Draw the bounds.
Ruiqi Mao46656e22018-07-20 14:18:50 -040082 if (drawBounds && fActorImage->renderOpacity() > 0.0f) {
83 // Get the bounds.
Ruiqi Maoc97a3392018-08-15 10:44:19 -040084 SkRect bounds = vertices->bounds();
Ruiqi Mao46656e22018-07-20 14:18:50 -040085
86 // Approximate bounds if not using immediate transforms.
87 if (!useImmediate) {
Ruiqi Maoc97a3392018-08-15 10:44:19 -040088 const SkRect originalBounds = fBones[0].mapRect(vertices->bounds());
Ruiqi Mao46656e22018-07-20 14:18:50 -040089 bounds = originalBounds;
90 for (size_t i = 1; i < fBones.size(); i++) {
Ruiqi Maoc97a3392018-08-15 10:44:19 -040091 const SkVertices::Bone& matrix = fBones[i];
Ruiqi Mao46656e22018-07-20 14:18:50 -040092 bounds.join(matrix.mapRect(originalBounds));
93 }
94 }
95
96 // Draw the bounds.
97 SkPaint paint;
98 paint.setStyle(SkPaint::kStroke_Style);
99 paint.setColor(0xFFFF0000);
100 canvas->drawRect(bounds, paint);
Ruiqi Maof5101492018-06-29 14:32:21 -0400101 }
Ruiqi Maof5101492018-06-29 14:32:21 -0400102 }
103
104 int drawOrder() const { return fActorImage->drawOrder(); }
105
106private:
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400107 void updateVertices(bool isVolatile) {
Ruiqi Maof5101492018-06-29 14:32:21 -0400108 // Update whether the image is skinned.
109 fSkinned = fActorImage->connectedBoneCount() > 0;
110
111 // Retrieve data from the image.
112 uint32_t vertexCount = fActorImage->vertexCount();
113 uint32_t vertexStride = fActorImage->vertexStride();
114 float* vertexData = fActorImage->vertices();
115 uint32_t indexCount = fActorImage->triangleCount() * 3;
116 uint16_t* indexData = fActorImage->triangles();
117
118 // Don't render if not visible.
119 if (!vertexCount || fActorImage->textureIndex() < 0) {
120 fPositions.clear();
121 fTexs.clear();
122 fBoneIdx.clear();
123 fBoneWgt.clear();
124 fIndices.clear();
125 return;
126 }
127
128 // Split the vertex data.
129 fPositions.resize(vertexCount);
130 fTexs.resize(vertexCount);
131 fIndices.resize(indexCount);
132 if (fSkinned) {
133 fBoneIdx.resize(vertexCount * 4);
134 fBoneWgt.resize(vertexCount * 4);
135 }
136 for (uint32_t i = 0; i < vertexCount; i ++) {
137 uint32_t j = i * vertexStride;
138
139 // Get the attributes.
140 float* attrPosition = vertexData + j;
141 float* attrTex = vertexData + j + 2;
142 float* attrBoneIdx = vertexData + j + 4;
143 float* attrBoneWgt = vertexData + j + 8;
144
145 // Get deformed positions if necessary.
146 if (fActorImage->doesAnimationVertexDeform()) {
147 attrPosition = fActorImage->animationDeformedVertices() + i * 2;
148 }
149
150 // Set the data.
151 fPositions[i].set(attrPosition[0], attrPosition[1]);
152 fTexs[i].set(attrTex[0] * fTexture->width(), attrTex[1] * fTexture->height());
153 if (fSkinned) {
154 for (uint32_t k = 0; k < 4; k ++) {
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400155 fBoneIdx[i][k] = static_cast<uint32_t>(attrBoneIdx[k]);
156 fBoneWgt[i][k] = attrBoneWgt[k];
Ruiqi Maof5101492018-06-29 14:32:21 -0400157 }
158 }
159 }
160 memcpy(fIndices.data(), indexData, indexCount * sizeof(uint16_t));
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400161
162 // Update the vertices object.
163 fVertices = SkVertices::MakeCopy(SkVertices::kTriangles_VertexMode,
164 vertexCount,
165 fPositions.data(),
166 fTexs.data(),
167 nullptr,
168 fBoneIdx.data(),
169 fBoneWgt.data(),
170 fIndices.size(),
171 fIndices.data(),
172 isVolatile);
Ruiqi Maof5101492018-06-29 14:32:21 -0400173 }
174
175 void updateBones() {
176 // NIMA matrices are a collection of 6 floats.
177 constexpr int kNIMAMatrixSize = 6;
178
179 // Set up the matrices for the first time.
180 if (fBones.size() == 0) {
181 int numMatrices = 1;
182 if (fSkinned) {
183 numMatrices = fActorImage->boneInfluenceMatricesLength() / kNIMAMatrixSize;
184 }
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400185
186 // Initialize all matrices to the identity matrix.
187 fBones.assign(numMatrices, {{ 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }});
Ruiqi Maof5101492018-06-29 14:32:21 -0400188 }
189
190 if (fSkinned) {
191 // Update the matrices.
192 float* matrixData = fActorImage->boneInfluenceMatrices();
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400193 memcpy(fBones.data(), matrixData, fBones.size() * kNIMAMatrixSize * sizeof(float));
Ruiqi Maof5101492018-06-29 14:32:21 -0400194 }
195
196 // Set the zero matrix to be the world transform.
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400197 memcpy(fBones.data(),
198 fActorImage->worldTransform().values(),
199 kNIMAMatrixSize * sizeof(float));
Ruiqi Maof5101492018-06-29 14:32:21 -0400200 }
201
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400202 void drawVerticesObject(SkVertices* vertices, SkCanvas* canvas, bool useBones) const {
Ruiqi Maof5101492018-06-29 14:32:21 -0400203 // Determine the blend mode.
204 SkBlendMode blendMode;
205 switch (fActorImage->blendMode()) {
206 case BlendMode::Off: {
207 blendMode = SkBlendMode::kSrc;
208 break;
209 }
210 case BlendMode::Normal: {
211 blendMode = SkBlendMode::kSrcOver;
212 break;
213 }
214 case BlendMode::Additive: {
215 blendMode = SkBlendMode::kPlus;
216 break;
217 }
218 case BlendMode::Multiply: {
219 blendMode = SkBlendMode::kMultiply;
220 break;
221 }
222 case BlendMode::Screen: {
223 blendMode = SkBlendMode::kScreen;
224 break;
225 }
226 }
227
228 // Set the opacity.
229 fPaint->setAlpha(static_cast<U8CPU>(fActorImage->renderOpacity() * 255));
230
231 // Draw the vertices.
232 if (useBones) {
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400233 canvas->drawVertices(vertices, fBones.data(), fBones.size(), blendMode, *fPaint);
Ruiqi Maof5101492018-06-29 14:32:21 -0400234 } else {
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400235 canvas->drawVertices(vertices, blendMode, *fPaint);
Ruiqi Maof5101492018-06-29 14:32:21 -0400236 }
237
238 // Reset the opacity.
239 fPaint->setAlpha(255);
240 }
241
Ruiqi Maof5101492018-06-29 14:32:21 -0400242private:
243 ActorImage* fActorImage;
244 SkImage* fTexture;
245 SkPaint* fPaint;
246
247 bool fSkinned;
248 std::vector<SkPoint> fPositions;
249 std::vector<SkPoint> fTexs;
250 std::vector<SkVertices::BoneIndices> fBoneIdx;
251 std::vector<SkVertices::BoneWeights> fBoneWgt;
252 std::vector<uint16_t> fIndices;
253
Ruiqi Maoc97a3392018-08-15 10:44:19 -0400254 std::vector<SkVertices::Bone> fBones;
255 sk_sp<SkVertices> fVertices;
Ruiqi Maof5101492018-06-29 14:32:21 -0400256
Ruiqi Mao46656e22018-07-20 14:18:50 -0400257 uint32_t fRenderFlags;
Ruiqi Maof5101492018-06-29 14:32:21 -0400258};
259
260//////////////////////////////////////////////////////////////////////////////////////////////////
261
262// Represents an Actor, or an animated character, in NIMA.
263class NIMAActor : public Actor {
264public:
265 NIMAActor(const std::string& basePath)
266 : fTexture(nullptr)
267 , fActorImages()
268 , fPaint()
269 , fAnimations() {
270 // Load the NIMA data.
271 std::string nimaPath((basePath + ".nima").c_str());
272 INHERITED::load(nimaPath);
273
274 // Load the image asset.
275 sk_sp<SkData> imageData = SkData::MakeFromFileName((basePath + ".png").c_str());
276 fTexture = SkImage::MakeFromEncoded(imageData);
277
278 // Create the paint.
279 fPaint.setShader(fTexture->makeShader(nullptr));
280 fPaint.setFilterQuality(SkFilterQuality::kLow_SkFilterQuality);
281
282 // Load the image nodes.
283 fActorImages.reserve(m_ImageNodeCount);
284 for (uint32_t i = 0; i < m_ImageNodeCount; i ++) {
285 fActorImages.emplace_back(m_ImageNodes[i], fTexture.get(), &fPaint);
286 }
287
288 // Sort the image nodes.
289 std::sort(fActorImages.begin(), fActorImages.end(), [](auto a, auto b) {
290 return a.drawOrder() < b.drawOrder();
291 });
292
293 // Get the list of animations.
294 fAnimations.reserve(m_AnimationsCount);
295 for (uint32_t i = 0; i < m_AnimationsCount; i ++) {
296 fAnimations.push_back(m_Animations[i].name());
297 }
298 }
299
Ruiqi Mao46656e22018-07-20 14:18:50 -0400300 void render(SkCanvas* canvas, uint32_t renderFlags) {
Ruiqi Maof5101492018-06-29 14:32:21 -0400301 // Render the image nodes.
302 for (auto& image : fActorImages) {
Ruiqi Mao46656e22018-07-20 14:18:50 -0400303 image.render(canvas, renderFlags);
Ruiqi Maof5101492018-06-29 14:32:21 -0400304 }
305 }
306
307 const std::vector<std::string>& getAnimations() const {
308 return fAnimations;
309 }
310
311private:
312 sk_sp<SkImage> fTexture;
313 std::vector<NIMAActorImage> fActorImages;
314 SkPaint fPaint;
315 std::vector<std::string> fAnimations;
316
317 typedef Actor INHERITED;
318};
319
320//////////////////////////////////////////////////////////////////////////////////////////////////
321
322NIMASlide::NIMASlide(const SkString& name, const SkString& path)
323 : fBasePath()
324 , fActor(nullptr)
325 , fPlaying(true)
326 , fTime(0.0f)
Ruiqi Mao46656e22018-07-20 14:18:50 -0400327 , fRenderFlags(0)
Ruiqi Maof5101492018-06-29 14:32:21 -0400328 , fAnimation(nullptr)
329 , fAnimationIndex(0) {
330 fName = name;
331
332 // Get the path components.
333 SkString baseName = SkOSPath::Basename(path.c_str());
334 baseName.resize(baseName.size() - 5);
335 SkString dirName = SkOSPath::Dirname(path.c_str());
336 SkString basePath = SkOSPath::Join(dirName.c_str(), baseName.c_str());
337
338 // Save the base path.
339 fBasePath = std::string(basePath.c_str());
340}
341
342NIMASlide::~NIMASlide() {}
343
344void NIMASlide::draw(SkCanvas* canvas) {
345 canvas->save();
346
Ruiqi Mao9a6e42f2018-07-09 14:16:56 -0400347 for (int i = 0; i < 10; i ++) {
348 for (int j = 0; j < 10; j ++) {
349 canvas->save();
Ruiqi Maof5101492018-06-29 14:32:21 -0400350
Ruiqi Mao9a6e42f2018-07-09 14:16:56 -0400351 canvas->translate(1250 - 250 * i, 1250 - 250 * j);
352 canvas->scale(0.5, -0.5);
353
354 // Render the actor.
Ruiqi Mao46656e22018-07-20 14:18:50 -0400355 fActor->render(canvas, fRenderFlags);
Ruiqi Mao9a6e42f2018-07-09 14:16:56 -0400356
357 canvas->restore();
358 }
359 }
Ruiqi Maof5101492018-06-29 14:32:21 -0400360
361 canvas->restore();
362
363 // Render the GUI.
364 this->renderGUI();
365}
366
367void NIMASlide::load(SkScalar winWidth, SkScalar winHeight) {
368 this->resetActor();
369}
370
371void NIMASlide::unload() {
372 // Discard resources.
373 fAnimation = nullptr;
374 fActor.reset(nullptr);
375}
376
377bool NIMASlide::animate(const SkAnimTimer& timer) {
378 // Apply the animation.
379 if (fAnimation) {
380 if (fPlaying) {
381 fTime = std::fmod(timer.secs(), fAnimation->max());
382 }
383 fAnimation->time(fTime);
384 fAnimation->apply(1.0f);
385 }
386 return true;
387}
388
389bool NIMASlide::onChar(SkUnichar c) {
390 return false;
391}
392
393bool NIMASlide::onMouse(SkScalar x, SkScalar y, Window::InputState state, uint32_t modifiers) {
394 return false;
395}
396
397void NIMASlide::resetActor() {
398 // Create the actor.
399 fActor = std::make_unique<NIMAActor>(fBasePath);
400
401 // Get the animation.
402 fAnimation = fActor->animationInstance(fActor->getAnimations()[fAnimationIndex]);
403}
404
405void NIMASlide::renderGUI() {
Ruiqi Mao46656e22018-07-20 14:18:50 -0400406 ImGui::SetNextWindowSize(ImVec2(300, 0));
Ruiqi Maof5101492018-06-29 14:32:21 -0400407 ImGui::Begin("NIMA");
408
409 // List of animations.
410 auto animations = const_cast<std::vector<std::string>&>(fActor->getAnimations());
411 ImGui::PushItemWidth(-1);
412 if (ImGui::ListBox("Animations",
413 &fAnimationIndex,
414 vector_getter,
415 reinterpret_cast<void*>(&animations),
416 animations.size(),
417 5)) {
418 resetActor();
419 }
420
421 // Playback control.
422 ImGui::Spacing();
423 if (ImGui::Button("Play")) {
424 fPlaying = true;
425 }
426 ImGui::SameLine();
427 if (ImGui::Button("Pause")) {
428 fPlaying = false;
429 }
430
431 // Time slider.
432 ImGui::PushItemWidth(-1);
433 ImGui::SliderFloat("Time", &fTime, 0.0f, fAnimation->max(), "Time: %.3f");
434
435 // Backend control.
Ruiqi Mao46656e22018-07-20 14:18:50 -0400436 int useImmediate = SkToBool(fRenderFlags & kImmediate_RenderFlag);
Ruiqi Maof5101492018-06-29 14:32:21 -0400437 ImGui::Spacing();
Ruiqi Mao46656e22018-07-20 14:18:50 -0400438 ImGui::RadioButton("Skia Backend", &useImmediate, 0);
439 ImGui::RadioButton("Immediate Backend", &useImmediate, 1);
440 if (useImmediate) {
441 fRenderFlags |= kImmediate_RenderFlag;
Ruiqi Maof5101492018-06-29 14:32:21 -0400442 } else {
Ruiqi Mao46656e22018-07-20 14:18:50 -0400443 fRenderFlags &= ~kImmediate_RenderFlag;
444 }
445
446 // Cache control.
447 bool useCache = SkToBool(fRenderFlags & kCache_RenderFlag);
448 ImGui::Spacing();
449 ImGui::Checkbox("Cache Vertices", &useCache);
450 if (useCache) {
451 fRenderFlags |= kCache_RenderFlag;
452 } else {
453 fRenderFlags &= ~kCache_RenderFlag;
454 }
455
456 // Bounding box toggle.
457 bool drawBounds = SkToBool(fRenderFlags & kBounds_RenderFlag);
458 ImGui::Spacing();
459 ImGui::Checkbox("Draw Bounds", &drawBounds);
460 if (drawBounds) {
461 fRenderFlags |= kBounds_RenderFlag;
462 } else {
463 fRenderFlags &= ~kBounds_RenderFlag;
Ruiqi Maof5101492018-06-29 14:32:21 -0400464 }
465
466 ImGui::End();
467}