Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / rive-cpp / skia / viewer / src / main.cpp
1
2 // Makes ure gl3w is included before glfw3
3 #include "GL/gl3w.h"
4
5 #define SK_GL
6 #include "GLFW/glfw3.h"
7
8 #include "GrBackendSurface.h"
9 #include "GrDirectContext.h"
10 #include "SkCanvas.h"
11 #include "SkColorSpace.h"
12 #include "SkSurface.h"
13 #include "SkTypes.h"
14 #include "gl/GrGLInterface.h"
15
16 #include "rive/animation/linear_animation_instance.hpp"
17 #include "rive/animation/state_machine_instance.hpp"
18 #include "rive/animation/state_machine_input_instance.hpp"
19 #include "rive/animation/state_machine_number.hpp"
20 #include "rive/animation/state_machine_bool.hpp"
21 #include "rive/animation/state_machine_trigger.hpp"
22 #include "rive/artboard.hpp"
23 #include "rive/file.hpp"
24 #include "rive/layout.hpp"
25 #include "rive/math/aabb.hpp"
26 #include "skia_factory.hpp"
27 #include "skia_renderer.hpp"
28
29 #include "imgui/backends/imgui_impl_glfw.h"
30 #include "imgui/backends/imgui_impl_opengl3.h"
31
32 #include <cmath>
33 #include <stdio.h>
34
35 rive::SkiaFactory skiaFactory;
36
37 std::string filename;
38 std::unique_ptr<rive::File> currentFile;
39 std::unique_ptr<rive::ArtboardInstance> artboardInstance;
40 std::unique_ptr<rive::Scene> currentScene;
41
42 // ImGui wants raw pointers to names, but our public API returns
43 // names as strings (by value), so we cache these names each time we
44 // load a file
45 std::vector<std::string> animationNames;
46 std::vector<std::string> stateMachineNames;
47
48 constexpr int REQUEST_DEFAULT_SCENE = -1;
49
50 #include <time.h>
51 double GetSecondsToday() {
52     time_t m_time;
53     time(&m_time);
54     struct tm tstruct;
55     gmtime_r(&m_time, &tstruct);
56
57     int hours = tstruct.tm_hour - 4;
58     if (hours < 0) {
59         hours += 12;
60     } else if (hours >= 12) {
61         hours -= 12;
62     }
63
64     auto secs = (double)hours * 60 * 60 +
65                 (double)tstruct.tm_min * 60 +
66                 (double)tstruct.tm_sec;
67 //    printf("%d %d %d\n", tstruct.tm_sec, tstruct.tm_min, hours);
68 //    printf("%g %g %g\n", secs, secs/60, secs/60/60);
69     return secs;
70 }
71
72 // We hold onto the file's bytes for the lifetime of the file, in case we want
73 // to change animations or state-machines, we just rebuild the rive::File from
74 // it.
75 std::vector<uint8_t> fileBytes;
76
77 int animationIndex = 0;
78 int stateMachineIndex = -1;
79
80 static void loadNames(const rive::Artboard* ab) {
81     animationNames.clear();
82     stateMachineNames.clear();
83     if (ab) {
84         for (size_t i = 0; i < ab->animationCount(); ++i) {
85             animationNames.push_back(ab->animationNameAt(i));
86         }
87         for (size_t i = 0; i < ab->stateMachineCount(); ++i) {
88             stateMachineNames.push_back(ab->stateMachineNameAt(i));
89         }
90     }
91 }
92
93 void initStateMachine(int index) {
94     assert(fileBytes.size() != 0);
95     auto file = rive::File::import(rive::toSpan(fileBytes), &skiaFactory);
96     if (!file) {
97         fileBytes.clear();
98         fprintf(stderr, "failed to import file\n");
99         return;
100     }
101
102     stateMachineIndex = -1;
103     animationIndex = -1;
104     currentScene = nullptr;
105     artboardInstance = nullptr;
106
107     currentFile = std::move(file);
108     artboardInstance = currentFile->artboardDefault();
109     artboardInstance->advance(0.0f);
110     loadNames(artboardInstance.get());
111
112     if (index < 0) {
113         currentScene = artboardInstance->defaultStateMachine();
114         index = artboardInstance->defaultStateMachineIndex();
115     }
116     if (!currentScene) {
117         if (index >= artboardInstance->stateMachineCount()) {
118             index = 0;
119         }
120         currentScene = artboardInstance->stateMachineAt(index);
121     }
122     if (!currentScene) {
123         index = -1;
124         currentScene = artboardInstance->animationAt(0);
125         animationIndex = 0;
126     }
127     stateMachineIndex = index;
128
129     if (currentScene) {
130         currentScene->inputCount();
131     }
132 }
133
134 void initAnimation(int index) {
135     animationIndex = index;
136     stateMachineIndex = -1;
137     assert(fileBytes.size() != 0);
138     auto file = rive::File::import(rive::toSpan(fileBytes), &skiaFactory);
139     if (!file) {
140         fileBytes.clear();
141         fprintf(stderr, "failed to import file\n");
142         return;
143     }
144     currentScene = nullptr;
145     artboardInstance = nullptr;
146
147     currentFile = std::move(file);
148     artboardInstance = currentFile->artboardDefault();
149     artboardInstance->advance(0.0f);
150     loadNames(artboardInstance.get());
151
152     if (index >= 0 && index < artboardInstance->animationCount()) {
153         currentScene = artboardInstance->animationAt(index);
154         currentScene->inputCount();
155     }
156 }
157
158 rive::Mat2D gInverseViewTransform;
159 rive::Vec2D lastWorldMouse;
160 static void glfwCursorPosCallback(GLFWwindow* window, double x, double y) {
161     float xscale, yscale;
162     glfwGetWindowContentScale(window, &xscale, &yscale);
163     lastWorldMouse = gInverseViewTransform * rive::Vec2D(x * xscale, y * yscale);
164     if (currentScene) {
165         currentScene->pointerMove(lastWorldMouse);
166     }
167 }
168 void glfwMouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
169     if (currentScene) {
170         switch (action) {
171             case GLFW_PRESS:
172                 currentScene->pointerDown(lastWorldMouse);
173                 break;
174             case GLFW_RELEASE:
175                 currentScene->pointerUp(lastWorldMouse);
176                 break;
177         }
178     }
179 }
180
181 void glfwErrorCallback(int error, const char* description) { puts(description); }
182
183 void glfwDropCallback(GLFWwindow* window, int count, const char** paths) {
184     // Just get the last dropped file for now...
185     filename = paths[count - 1];
186
187     FILE* fp = fopen(filename.c_str(), "rb");
188     fseek(fp, 0, SEEK_END);
189     size_t size = ftell(fp);
190     fseek(fp, 0, SEEK_SET);
191     fileBytes.resize(size);
192     if (fread(fileBytes.data(), 1, size, fp) != size) {
193         fileBytes.clear();
194         fprintf(stderr, "failed to read all of %s\n", filename.c_str());
195         return;
196     }
197     initStateMachine(REQUEST_DEFAULT_SCENE);
198 }
199
200 int main() {
201     if (!glfwInit()) {
202         fprintf(stderr, "Failed to initialize glfw.\n");
203         return 1;
204     }
205     glfwSetErrorCallback(glfwErrorCallback);
206
207     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
208     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
209     GLFWwindow* window = glfwCreateWindow(1280, 720, "Rive Viewer", NULL, NULL);
210     if (window == nullptr) {
211         fprintf(stderr, "Failed to make window or GL.\n");
212         glfwTerminate();
213         return 1;
214     }
215
216     glfwSetDropCallback(window, glfwDropCallback);
217     glfwSetCursorPosCallback(window, glfwCursorPosCallback);
218     glfwSetMouseButtonCallback(window, glfwMouseButtonCallback);
219     glfwMakeContextCurrent(window);
220     if (gl3wInit() != 0) {
221         fprintf(stderr, "Failed to make initialize gl3w.\n");
222         glfwTerminate();
223         return 1;
224     }
225     // Enable VSYNC.
226     glfwSwapInterval(1);
227
228     // Setup ImGui
229     ImGui::CreateContext();
230     ImGuiIO& io = ImGui::GetIO();
231     (void)io;
232
233     ImGui::StyleColorsDark();
234     ImGui_ImplGlfw_InitForOpenGL(window, true);
235     ImGui_ImplOpenGL3_Init("#version 150");
236     io.Fonts->AddFontDefault();
237
238     // Setup Skia
239     GrContextOptions options;
240     sk_sp<GrDirectContext> context = GrDirectContext::MakeGL(nullptr, options);
241     GrGLFramebufferInfo framebufferInfo;
242     framebufferInfo.fFBOID = 0;
243     framebufferInfo.fFormat = GL_RGBA8;
244
245     sk_sp<SkSurface> surface;
246     SkCanvas* canvas = nullptr;
247
248     // Render loop.
249     int width = 0, height = 0;
250     int lastScreenWidth = 0, lastScreenHeight = 0;
251     double lastTime = glfwGetTime();
252     while (!glfwWindowShouldClose(window)) {
253         glfwGetFramebufferSize(window, &width, &height);
254
255         // Update surface.
256         if (!surface || width != lastScreenWidth || height != lastScreenHeight) {
257             lastScreenWidth = width;
258             lastScreenHeight = height;
259
260             SkColorType colorType =
261                 kRGBA_8888_SkColorType; // GrColorTypeToSkColorType(GrPixelConfigToColorType(kRGBA_8888_GrPixelConfig));
262             //
263             // if (kRGBA_8888_GrPixelConfig == kSkia8888_GrPixelConfig)
264             // {
265             //  colorType = kRGBA_8888_SkColorType;
266             // }
267             // else
268             // {
269             //  colorType = kBGRA_8888_SkColorType;
270             // }
271
272             GrBackendRenderTarget backendRenderTarget(width,
273                                                       height,
274                                                       0, // sample count
275                                                       0, // stencil bits
276                                                       framebufferInfo);
277
278             surface = SkSurface::MakeFromBackendRenderTarget(context.get(),
279                                                              backendRenderTarget,
280                                                              kBottomLeft_GrSurfaceOrigin,
281                                                              colorType,
282                                                              nullptr,
283                                                              nullptr);
284             if (!surface) {
285                 fprintf(stderr, "Failed to create Skia surface\n");
286                 return 1;
287             }
288             canvas = surface->getCanvas();
289         }
290
291         double time = glfwGetTime();
292         float elapsed = (float)(time - lastTime);
293         lastTime = time;
294
295         // Clear screen.
296         SkPaint paint;
297         paint.setColor(SK_ColorDKGRAY);
298         canvas->drawPaint(paint);
299
300         if (currentScene) {
301             // See if we can "set the time" e.g. clock statemachine
302             if (auto num = currentScene->getNumber("isTime")) {
303                 num->value(GetSecondsToday()/60/60);
304             }
305
306             currentScene->advanceAndApply(elapsed);
307
308             rive::SkiaRenderer renderer(canvas);
309             renderer.save();
310
311             auto viewTransform = rive::computeAlignment(rive::Fit::contain,
312                                                         rive::Alignment::center,
313                                                         rive::AABB(0, 0, width, height),
314                                                         currentScene->bounds());
315             renderer.transform(viewTransform);
316             // Store the inverse view so we can later go from screen to world.
317             gInverseViewTransform = viewTransform.invertOrIdentity();
318             // post_mouse_event(artboard.get(), canvas->getTotalMatrix());
319
320             currentScene->draw(&renderer);
321             renderer.restore();
322         }
323         context->flush();
324
325         ImGui_ImplOpenGL3_NewFrame();
326         ImGui_ImplGlfw_NewFrame();
327         ImGui::NewFrame();
328
329         if (artboardInstance != nullptr) {
330             ImGui::Begin(filename.c_str(), nullptr);
331             if (ImGui::ListBox(
332                     "Animations",
333                     &animationIndex,
334                     [](void* data, int index, const char** name) {
335                         *name = animationNames[index].c_str();
336                         return true;
337                     },
338                     artboardInstance.get(),
339                     animationNames.size(),
340                     4))
341             {
342                 stateMachineIndex = -1;
343                 initAnimation(animationIndex);
344             }
345             if (ImGui::ListBox(
346                     "State Machines",
347                     &stateMachineIndex,
348                     [](void* data, int index, const char** name) {
349                         *name = stateMachineNames[index].c_str();
350                         return true;
351                     },
352                     artboardInstance.get(),
353                     stateMachineNames.size(),
354                     4))
355             {
356                 animationIndex = -1;
357                 initStateMachine(stateMachineIndex);
358             }
359             if (currentScene != nullptr) {
360
361                 ImGui::Columns(2);
362                 ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() * 0.6666);
363
364                 for (int i = 0; i < currentScene->inputCount(); i++) {
365                     auto inputInstance = currentScene->input(i);
366
367                     if (inputInstance->input()->is<rive::StateMachineNumber>()) {
368                         // ImGui requires names as id's, use ## to hide the
369                         // label but still give it an id.
370                         char label[256];
371                         snprintf(label, 256, "##%u", i);
372
373                         auto number = static_cast<rive::SMINumber*>(inputInstance);
374                         float v = number->value();
375                         ImGui::InputFloat(label, &v, 1.0f, 2.0f, "%.3f");
376                         number->value(v);
377                         ImGui::NextColumn();
378                     } else if (inputInstance->input()->is<rive::StateMachineTrigger>()) {
379                         // ImGui requires names as id's, use ## to hide the
380                         // label but still give it an id.
381                         char label[256];
382                         snprintf(label, 256, "Fire##%u", i);
383                         if (ImGui::Button(label)) {
384                             auto trigger = static_cast<rive::SMITrigger*>(inputInstance);
385                             trigger->fire();
386                         }
387                         ImGui::NextColumn();
388                     } else if (inputInstance->input()->is<rive::StateMachineBool>()) {
389                         // ImGui requires names as id's, use ## to hide the
390                         // label but still give it an id.
391                         char label[256];
392                         snprintf(label, 256, "##%u", i);
393                         auto boolInput = static_cast<rive::SMIBool*>(inputInstance);
394                         bool value = boolInput->value();
395
396                         ImGui::Checkbox(label, &value);
397                         boolInput->value(value);
398                         ImGui::NextColumn();
399                     }
400                     ImGui::Text("%s", inputInstance->input()->name().c_str());
401                     ImGui::NextColumn();
402                 }
403
404                 ImGui::Columns(1);
405             }
406             ImGui::End();
407
408         } else {
409             ImGui::Text("Drop a .riv file to preview.");
410         }
411
412         ImGui::Render();
413         ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
414
415         glfwSwapBuffers(window);
416         glfwPollEvents();
417     }
418
419     // Cleanup Skia.
420     surface = nullptr;
421     context = nullptr;
422
423     ImGui_ImplGlfw_Shutdown();
424
425     // Cleanup GLFW.
426     glfwDestroyWindow(window);
427     glfwTerminate();
428
429     return 0;
430 }