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 "NIMASlide.h"
9 
10 #include "Resources.h"
11 #include "SkAnimTimer.h"
12 #include "SkOSPath.h"
13 #include "imgui.h"
14 #include "nima/NimaActor.h"
15 
16 #include <algorithm>
17 #include <cmath>
18 
19 using namespace sk_app;
20 using namespace nima;
21 
22 // ImGui expects an array of const char* when displaying a ListBox. This function is for an
23 // overload of ImGui::ListBox that takes a getter so that ListBox works with
24 // std::vector<std::string>.
vector_getter(void * v,int index,const char ** out)25 static bool vector_getter(void* v, int index, const char** out) {
26     auto vector = reinterpret_cast<std::vector<std::string>*>(v);
27     *out = vector->at(index).c_str();
28     return true;
29 }
30 
31 //////////////////////////////////////////////////////////////////////////////////////////////////
32 
NIMASlide(const SkString & name,const SkString & path)33 NIMASlide::NIMASlide(const SkString& name, const SkString& path)
34         : fBasePath()
35         , fActor(nullptr)
36         , fAnimationIndex(0)
37         , fPlaying(true)
38         , fTime(0.0f)
39         , fRenderFlags(0) {
40     fName = name;
41 
42     // Get the path components.
43     SkString baseName = SkOSPath::Basename(path.c_str());
44     baseName.resize(baseName.size() - 5);
45     SkString dirName = SkOSPath::Dirname(path.c_str());
46     SkString basePath = SkOSPath::Join(dirName.c_str(), baseName.c_str());
47 
48     // Save the base path.
49     fBasePath = std::string(basePath.c_str());
50 }
51 
~NIMASlide()52 NIMASlide::~NIMASlide() {}
53 
getDimensions() const54 SkISize NIMASlide::getDimensions() const {
55     return SkISize::MakeEmpty(); // TODO
56 }
57 
draw(SkCanvas * canvas)58 void NIMASlide::draw(SkCanvas* canvas) {
59     canvas->save();
60 
61     for (int i = 0; i < 10; i ++) {
62         for (int j = 0; j < 10; j ++) {
63             canvas->save();
64 
65             canvas->translate(1250 - 250 * i, 1250 - 250 * j);
66             canvas->scale(0.5, -0.5);
67 
68             // Render the actor.
69             fActor->setAnimation(fAnimationIndex);
70             fActor->render(canvas, fRenderFlags);
71 
72             canvas->restore();
73         }
74     }
75 
76     canvas->restore();
77 
78     // Render the GUI.
79     this->renderGUI();
80 }
81 
load(SkScalar winWidth,SkScalar winHeight)82 void NIMASlide::load(SkScalar winWidth, SkScalar winHeight) {
83     this->resetActor();
84 }
85 
unload()86 void NIMASlide::unload() {
87     // Discard resources.
88     fActor.reset(nullptr);
89 }
90 
animate(const SkAnimTimer & timer)91 bool NIMASlide::animate(const SkAnimTimer& timer) {
92     // Apply the animation.
93     if (fActor) {
94         float time = std::fmod(timer.secs(), fActor->duration());
95         fActor->seek(time);
96     }
97     return true;
98 }
99 
onChar(SkUnichar c)100 bool NIMASlide::onChar(SkUnichar c) {
101     return false;
102 }
103 
onMouse(SkScalar x,SkScalar y,Window::InputState state,uint32_t modifiers)104 bool NIMASlide::onMouse(SkScalar x, SkScalar y, Window::InputState state, uint32_t modifiers) {
105     return false;
106 }
107 
resetActor()108 void NIMASlide::resetActor() {
109     // Create the actor.
110     std::string nimaPath = fBasePath + ".nima";
111     std::string texturePath = fBasePath + ".png";
112 
113     fActor = std::make_unique<NimaActor>(nimaPath, texturePath);
114 }
115 
renderGUI()116 void NIMASlide::renderGUI() {
117     ImGui::SetNextWindowSize(ImVec2(300, 0));
118     ImGui::Begin("NIMA");
119 
120     // List of animations.
121     auto animations = const_cast<std::vector<std::string>&>(fActor->getAnimationNames());
122     ImGui::PushItemWidth(-1);
123     if (ImGui::ListBox("Animations",
124                        &fAnimationIndex,
125                        vector_getter,
126                        reinterpret_cast<void*>(&animations),
127                        animations.size(),
128                        5)) {
129         resetActor();
130     }
131 
132     // Playback control.
133     ImGui::Spacing();
134     if (ImGui::Button("Play")) {
135         fPlaying = true;
136     }
137     ImGui::SameLine();
138     if (ImGui::Button("Pause")) {
139         fPlaying = false;
140     }
141 
142     // Time slider.
143     ImGui::PushItemWidth(-1);
144     ImGui::SliderFloat("Time", &fTime, 0.0f, fActor->duration(), "Time: %.3f");
145 
146     // Backend control.
147     int useImmediate = SkToBool(fRenderFlags & kImmediate_RenderFlag);
148     ImGui::Spacing();
149     ImGui::RadioButton("Skia Backend", &useImmediate, 0);
150     ImGui::RadioButton("Immediate Backend", &useImmediate, 1);
151     if (useImmediate) {
152         fRenderFlags |= kImmediate_RenderFlag;
153     } else {
154         fRenderFlags &= ~kImmediate_RenderFlag;
155     }
156 
157     // Cache control.
158     bool useCache = SkToBool(fRenderFlags & kCache_RenderFlag);
159     ImGui::Spacing();
160     ImGui::Checkbox("Cache Vertices", &useCache);
161     if (useCache) {
162         fRenderFlags |= kCache_RenderFlag;
163     } else {
164         fRenderFlags &= ~kCache_RenderFlag;
165     }
166 
167     // Bounding box toggle.
168     bool drawBounds = SkToBool(fRenderFlags & kBounds_RenderFlag);
169     ImGui::Spacing();
170     ImGui::Checkbox("Draw Bounds", &drawBounds);
171     if (drawBounds) {
172         fRenderFlags |= kBounds_RenderFlag;
173     } else {
174         fRenderFlags &= ~kBounds_RenderFlag;
175     }
176 
177     ImGui::End();
178 }
179