blob: bb443d2b4a5c10f5a2135d1cb30adc20c29a5a03 [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#include "rive/animation/linear_animation_instance.hpp"
#include "rive/animation/state_machine_instance.hpp"
#include "rive/animation/state_machine_input_instance.hpp"
#include "rive/animation/state_machine_number.hpp"
#include "rive/animation/state_machine_bool.hpp"
#include "rive/animation/state_machine_trigger.hpp"
#include "rive/artboard.hpp"
#include "rive/file.hpp"
#include "rive/layout.hpp"
#include "rive/math/aabb.hpp"
#include "cg_skia_factory.hpp"
#include "skia_renderer.hpp"
#include "viewer_content.hpp"
#include "../src/render_counter.hpp"
// ImGui wants raw pointers to names, but our public API returns
// names as strings (by value), so we cache these names each time we
// load a file
std::vector<std::string> animationNames;
std::vector<std::string> stateMachineNames;
constexpr int REQUEST_DEFAULT_SCENE = -1;
#include <time.h>
double GetSecondsToday() {
time_t m_time;
time(&m_time);
struct tm tstruct;
gmtime_r(&m_time, &tstruct);
int hours = tstruct.tm_hour - 4;
if (hours < 0) {
hours += 12;
} else if (hours >= 12) {
hours -= 12;
}
auto secs = (double)hours * 60 * 60 +
(double)tstruct.tm_min * 60 +
(double)tstruct.tm_sec;
// printf("%d %d %d\n", tstruct.tm_sec, tstruct.tm_min, hours);
// printf("%g %g %g\n", secs, secs/60, secs/60/60);
return secs;
}
// We hold onto the file's bytes for the lifetime of the file, in case we want
// to change animations or state-machines, we just rebuild the rive::File from
// it.
std::vector<uint8_t> fileBytes;
static void loadNames(const rive::Artboard* ab) {
animationNames.clear();
stateMachineNames.clear();
if (ab) {
for (size_t i = 0; i < ab->animationCount(); ++i) {
animationNames.push_back(ab->animationNameAt(i));
}
for (size_t i = 0; i < ab->stateMachineCount(); ++i) {
stateMachineNames.push_back(ab->stateMachineNameAt(i));
}
}
}
class SceneContent : public ViewerContent {
std::string m_filename;
std::unique_ptr<rive::File> m_file;
std::unique_ptr<rive::ArtboardInstance> m_artboardInstance;
std::unique_ptr<rive::Scene> m_currentScene;
int m_animationIndex = 0;
int m_stateMachineIndex = -1;
int m_width = 0, m_height = 0;
rive::Vec2D m_lastPointer;
rive::Mat2D m_inverseViewTransform;
void initStateMachine(int index) {
m_stateMachineIndex = -1;
m_animationIndex = -1;
m_currentScene = nullptr;
m_artboardInstance = nullptr;
m_artboardInstance = m_file->artboardDefault();
m_artboardInstance->advance(0.0f);
loadNames(m_artboardInstance.get());
if (index < 0) {
m_currentScene = m_artboardInstance->defaultStateMachine();
index = m_artboardInstance->defaultStateMachineIndex();
}
if (!m_currentScene) {
if (index >= m_artboardInstance->stateMachineCount()) {
index = 0;
}
m_currentScene = m_artboardInstance->stateMachineAt(index);
}
if (!m_currentScene) {
index = -1;
m_currentScene = m_artboardInstance->animationAt(0);
m_animationIndex = 0;
}
m_stateMachineIndex = index;
if (m_currentScene) {
m_currentScene->inputCount();
}
rive::RenderCounter::globalCounter().dump("After loading file");
}
void initAnimation(int index) {
m_stateMachineIndex = -1;
m_animationIndex = -1;
m_currentScene = nullptr;
m_artboardInstance = nullptr;
m_artboardInstance = m_file->artboardDefault();
m_artboardInstance->advance(0.0f);
loadNames(m_artboardInstance.get());
if (index >= 0 && index < m_artboardInstance->animationCount()) {
m_animationIndex = index;
m_currentScene = m_artboardInstance->animationAt(index);
m_currentScene->inputCount();
}
rive::RenderCounter::globalCounter().dump("After loading file");
}
public:
SceneContent(const char filename[], std::unique_ptr<rive::File> file) :
m_filename(filename),
m_file(std::move(file))
{
initStateMachine(REQUEST_DEFAULT_SCENE);
}
~SceneContent() override {
rive::RenderCounter::globalCounter().dump("After deleting file");
}
void handlePointerMove(float x, float y) override {
m_lastPointer = m_inverseViewTransform * rive::Vec2D(x, y);
if (m_currentScene) {
m_currentScene->pointerMove(m_lastPointer);
}
}
void handlePointerDown() override {
if (m_currentScene) {
m_currentScene->pointerDown(m_lastPointer);
}
}
void handlePointerUp() override {
if (m_currentScene) {
m_currentScene->pointerUp(m_lastPointer);
}
}
void handleResize(int width, int height) override {
m_width = width;
m_height = height;
}
void handleDraw(SkCanvas* canvas, double elapsed) override {
if (m_currentScene) {
// See if we can "set the time" e.g. clock statemachine
if (auto num = m_currentScene->getNumber("isTime")) {
num->value(GetSecondsToday()/60/60);
}
m_currentScene->advanceAndApply(elapsed);
rive::SkiaRenderer renderer(canvas);
renderer.save();
auto viewTransform = rive::computeAlignment(rive::Fit::contain,
rive::Alignment::center,
rive::AABB(0, 0, m_width, m_height),
m_currentScene->bounds());
renderer.transform(viewTransform);
// Store the inverse view so we can later go from screen to world.
m_inverseViewTransform = viewTransform.invertOrIdentity();
// post_mouse_event(artboard.get(), canvas->getTotalMatrix());
m_currentScene->draw(&renderer);
renderer.restore();
}
}
void handleImgui() override {
if (m_artboardInstance != nullptr) {
ImGui::Begin(m_filename.c_str(), nullptr);
if (ImGui::ListBox(
"Animations",
&m_animationIndex,
[](void* data, int index, const char** name) {
*name = animationNames[index].c_str();
return true;
},
m_artboardInstance.get(),
animationNames.size(),
4))
{
m_stateMachineIndex = -1;
initAnimation(m_animationIndex);
}
if (ImGui::ListBox(
"State Machines",
&m_stateMachineIndex,
[](void* data, int index, const char** name) {
*name = stateMachineNames[index].c_str();
return true;
},
m_artboardInstance.get(),
stateMachineNames.size(),
4))
{
m_animationIndex = -1;
initStateMachine(m_stateMachineIndex);
}
if (m_currentScene != nullptr) {
ImGui::Columns(2);
ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() * 0.6666);
for (int i = 0; i < m_currentScene->inputCount(); i++) {
auto inputInstance = m_currentScene->input(i);
if (inputInstance->input()->is<rive::StateMachineNumber>()) {
// ImGui requires names as id's, use ## to hide the
// label but still give it an id.
char label[256];
snprintf(label, 256, "##%u", i);
auto number = static_cast<rive::SMINumber*>(inputInstance);
float v = number->value();
ImGui::InputFloat(label, &v, 1.0f, 2.0f, "%.3f");
number->value(v);
ImGui::NextColumn();
} else if (inputInstance->input()->is<rive::StateMachineTrigger>()) {
// ImGui requires names as id's, use ## to hide the
// label but still give it an id.
char label[256];
snprintf(label, 256, "Fire##%u", i);
if (ImGui::Button(label)) {
auto trigger = static_cast<rive::SMITrigger*>(inputInstance);
trigger->fire();
}
ImGui::NextColumn();
} else if (inputInstance->input()->is<rive::StateMachineBool>()) {
// ImGui requires names as id's, use ## to hide the
// label but still give it an id.
char label[256];
snprintf(label, 256, "##%u", i);
auto boolInput = static_cast<rive::SMIBool*>(inputInstance);
bool value = boolInput->value();
ImGui::Checkbox(label, &value);
boolInput->value(value);
ImGui::NextColumn();
}
ImGui::Text("%s", inputInstance->input()->name().c_str());
ImGui::NextColumn();
}
ImGui::Columns(1);
}
ImGui::End();
} else {
ImGui::Text("Drop a .riv file to preview.");
}
}
};
rive::CGSkiaFactory skiaFactory;
std::unique_ptr<ViewerContent> ViewerContent::Scene(const char filename[]) {
auto bytes = LoadFile(filename);
if (auto file = rive::File::import(rive::toSpan(bytes), &skiaFactory)) {
return std::make_unique<SceneContent>(filename, std::move(file));
}
return nullptr;
}