#include "pch.h" #include "node_panel_animation.h" #include "app_core/document_animation.h" #include "node_button.h" #include "node_button_custom.h" #include "renderer_gl/opengl_capabilities.h" #include "canvas.h" #include "app.h" Node* NodePanelAnimation::clone_instantiate() const { return new this_class; } void NodePanelAnimation::clone_finalize(Node* dest) const { parent::clone_finalize(dest); auto n = static_cast(dest); n->init_controls(); } void NodePanelAnimation::init() { parent::init(); init_template_file("data/dialogs/panel-animation.xml", "tpl-panel-animation"); init_controls(); } void NodePanelAnimation::execute_animation_plan(const pp::app::DocumentAnimationOperationPlan& plan, Layer* layer) { class LegacyAnimationServices final : public pp::app::DocumentAnimationServices { public: LegacyAnimationServices(NodePanelAnimation& panel, Layer* layer) noexcept : panel_(panel) , layer_(layer) { } void add_frame() override { Canvas::I->layer().add_frame(); } void duplicate_frame(int selected_frame) override { if (layer_) layer_->duplicate_frame(selected_frame); } void remove_frame(int selected_frame, int target_frame) override { if (!layer_) return; layer_->remove_frame(selected_frame); panel_.m_selected_frame_index = target_frame; } void set_frame_duration(int selected_frame, int duration) override { if (layer_) layer_->set_frame_duration(selected_frame, duration); } int move_frame(int selected_frame, int move_offset) override { if (!layer_) return selected_frame; panel_.m_selected_frame_index = layer_->move_frame_offset(selected_frame, move_offset); return panel_.m_selected_frame_index; } void select_frame(std::uint32_t layer_id, int layer_index, int selected_frame) override { panel_.m_selected_frame_layer_id = layer_id; panel_.m_selected_frame_index = selected_frame; panel_.m_timeline->m_frame = selected_frame; } void select_layer(int layer_index) override { App::I->layers->handle_layer_selected(App::I->layers->get_layer_at(layer_index)); } void goto_frame(int target_frame) override { Canvas::I->anim_goto_frame(target_frame); } void set_timeline_frame(int target_frame) override { panel_.m_timeline->m_frame = target_frame; } void set_onion_size(int onion_size) override { panel_.m_timeline->m_onion_size = onion_size; } void capture_playback_restore_mode() override { playback_restore_mode() = Canvas::I->m_current_mode; } void enter_playback_camera_mode() override { Canvas::set_mode(kCanvasMode::Camera); } void restore_playback_canvas_mode() override { Canvas::set_mode(playback_restore_mode()); } void set_playback_active(bool active) override { panel_.btn_play->set_active(active); } void reset_playback_timer() override { panel_.m_playback_timer = 0; } void set_playback_idle_ms(int idle_ms) override { App::I->idle_ms = idle_ms; } void update_canvas_animation() override { Canvas::I->anim_update(); } void update_frame_status() override { panel_.update_frames(); } void reload_animation_layers() override { panel_.load_layers(); } void mark_unsaved() override { Canvas::I->m_unsaved = true; } private: static kCanvasMode& playback_restore_mode() { static auto mode = Canvas::I->m_current_mode; return mode; } NodePanelAnimation& panel_; Layer* layer_ = nullptr; }; LegacyAnimationServices services(*this, layer); const auto status = pp::app::execute_animation_operation_plan(plan, services); if (!status.ok()) LOG("Animation panel action failed: %s", status.message); } pp::app::DocumentAnimationPanelState NodePanelAnimation::animation_panel_state() const { return pp::app::DocumentAnimationPanelState { .total_duration = Canvas::I->anim_duration(), .current_frame = Canvas::I->m_anim_frame, .playback_active = btn_play->is_active(), }; } void NodePanelAnimation::init_controls() { m_layers_container = find("layers"); m_frames_container = find("frames"); m_timeline = find("timeline"); btn_add = find("btn-add"); btn_remove = find("btn-remove"); btn_up = find("btn-up"); btn_down = find("btn-down"); btn_left = find("btn-left"); btn_right = find("btn-right"); btn_duplicate = find("btn-duplicate"); btn_next = find("btn-next"); btn_prev = find("btn-prev"); btn_play = find("btn-play"); m_fps = find("fps"); m_onion = find("onion"); m_frame_label = find("frame-index"); btn_add->on_click = [this](Node*) { const auto plan = pp::app::plan_animation_add_frame( Canvas::I->layer().frames_count(), Canvas::I->m_anim_frame); if (!plan) return; execute_animation_plan(plan.value()); }; btn_duplicate->on_click = [this](Node*) { if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id)) { const auto plan = pp::app::plan_animation_duplicate_frame( layer->frames_count(), m_selected_frame_index); if (!plan) return; execute_animation_plan(plan.value(), layer.get()); } }; btn_remove->on_click = [this](Node*) { if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id)) { const auto plan = pp::app::plan_animation_remove_frame( layer->frames_count(), m_selected_frame_index); if (!plan) return; execute_animation_plan(plan.value(), layer.get()); } }; btn_up->on_click = [this](Node*) { if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id)) { const auto plan = pp::app::plan_animation_adjust_duration( layer->frames_count(), m_selected_frame_index, layer->frame_duration(m_selected_frame_index), 1); if (!plan) return; execute_animation_plan(plan.value(), layer.get()); } }; btn_down->on_click = [this](Node*) { if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id)) { const auto plan = pp::app::plan_animation_adjust_duration( layer->frames_count(), m_selected_frame_index, layer->frame_duration(m_selected_frame_index), -1); if (!plan) return; execute_animation_plan(plan.value(), layer.get()); } }; btn_left->on_click = [this](Node*) { if (!m_selected_frame) return; if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id)) { const auto plan = pp::app::plan_animation_move_frame( layer->frames_count(), m_selected_frame_index, -1); if (!plan) return; execute_animation_plan(plan.value(), layer.get()); } }; btn_right->on_click = [this](Node*) { if (!m_selected_frame) return; if (auto layer = Canvas::I->layer_with_id(m_selected_frame_layer_id)) { const auto plan = pp::app::plan_animation_move_frame( layer->frames_count(), m_selected_frame_index, 1); if (!plan) return; execute_animation_plan(plan.value(), layer.get()); } }; m_onion->on_select = [this] (Node* target, int index) { const auto plan = pp::app::plan_animation_onion_size(m_onion->get_int()); if (!plan) return; execute_animation_plan(plan.value()); }; m_timeline->on_frame_changed = [this] (NodeAnimationTimeline* target, int frame) { const auto plan = pp::app::plan_animation_panel_action( pp::app::DocumentAnimationPanelAction::goto_frame, animation_panel_state(), frame); if (!plan) return; LOG("goto frame %d", plan.value().target_frame); execute_animation_plan(plan.value()); }; btn_next->on_click = [this] (Node* target) { const auto plan = pp::app::plan_animation_panel_action( pp::app::DocumentAnimationPanelAction::next_frame, animation_panel_state()); if (!plan) return; execute_animation_plan(plan.value()); }; btn_prev->on_click = [this](Node* target) { const auto plan = pp::app::plan_animation_panel_action( pp::app::DocumentAnimationPanelAction::previous_frame, animation_panel_state()); if (!plan) return; execute_animation_plan(plan.value()); }; btn_play->on_click = [this] (Node*) { const auto plan = pp::app::plan_animation_panel_action( pp::app::DocumentAnimationPanelAction::toggle_playback, animation_panel_state()); if (plan) execute_animation_plan(plan.value()); }; } void NodePanelAnimation::load_layers() { if (!added_to_root()) return; m_layers_container->remove_all_children(); m_frames_container->remove_all_children(); auto& layers = Canvas::I->m_layers; m_selected_frame = nullptr; for (int i = 0; i < layers.size(); i++) { auto l = m_layers_container->add_child(); l->set_text(layers[i]->m_name); l->set_selected(Canvas::I->m_current_layer_idx == i); l->set_chekcbox(layers[i]->m_visible); auto film = m_frames_container->add_child_ref(); for (int fi = 0; fi < layers[i]->frames_count(); fi++) { auto b = film->add_frame(layers[i]->frame_duration(fi)); if (m_selected_frame_layer_id == layers[i]->id && m_selected_frame_index == fi) { b->set_active(true); m_selected_frame = b.get(); } b->on_click = [this, fi, lid=layers[i]->id, i] (Node* target) { auto frame = static_cast(target); if (m_selected_frame) m_selected_frame->set_active(false); frame->set_active(true); m_selected_frame = frame; const auto plan = pp::app::plan_animation_select_frame( Canvas::I->m_layers[i]->frames_count(), i, lid, fi); if (plan) execute_animation_plan(plan.value(), Canvas::I->m_layers[i].get()); }; } } m_timeline->m_frame = Canvas::I->m_anim_frame; m_timeline->m_onion_size = m_onion->get_int(); update_frames(); } void NodePanelAnimation::update_frames() { int total_frames = Canvas::I->anim_duration(); int digits = (int)floor(glm::log(total_frames)); m_frame_label->set_text_format("%0*d/%d", digits, m_timeline->m_frame + 1, total_frames); } void NodePanelAnimation::on_tick(float dt) { parent::on_tick(dt); if (btn_play->is_active()) { m_playback_timer += dt; if (m_playback_timer > (1.f / m_fps->get_float())) { m_playback_timer = 0; const auto plan = pp::app::plan_animation_panel_action( pp::app::DocumentAnimationPanelAction::playback_step, animation_panel_state()); if (plan) execute_animation_plan(plan.value()); } } } void NodePanelAnimation::added(Node* parent) { parent::added(parent); load_layers(); } ////////////////////////////////////////////////////////////////////////// Node* NodeAnimationLayer::clone_instantiate() const { return new this_class; } void NodeAnimationLayer::clone_finalize(Node* dest) const { parent::clone_finalize(dest); auto n = static_cast(dest); n->init_controls(); } void NodeAnimationLayer::init() { parent::init(); init_template_file("data/dialogs/panel-animation.xml", "tpl-layer"); init_controls(); } void NodeAnimationLayer::init_controls() { m_label = find("label"); m_visibility = find("cb"); } void NodeAnimationLayer::draw() { auto c = m_selected ? m_color_selected : m_color_normal; m_thinkness = m_selected ? 1.f : 0.f; m_color = m_mouse_inside ? m_color_hover : c; parent::draw(); } ////////////////////////////////////////////////////////////////////////// void NodeAnimationTimeline::draw() { parent::draw(); ShaderManager::use(kShader::Color); glDisable(pp::renderer::gl::blend_state()); float step = 35.f; glm::vec2 cur_pos = { m_pos.x + step * m_frame + step * 0.5f, m_pos.y + m_size.y * 0.5f }; ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(glm::vec3(m_cursor_color) * 0.5f, 1.f)); ShaderManager::u_mat4(kShaderUniform::MVP, m_proj * glm::translate(glm::vec3(cur_pos, 0)) * glm::scale(glm::vec3(step * m_onion_size * 2.f, m_size.y * 0.5f, 1)) ); m_plane.draw_fill(); ShaderManager::u_vec4(kShaderUniform::Col, m_cursor_color); ShaderManager::u_mat4(kShaderUniform::MVP, m_proj * glm::translate(glm::vec3(cur_pos, 0)) * glm::scale(glm::vec3(step * 0.25f, m_size.y * 0.75f, 1)) ); m_plane.draw_fill(); /* ShaderManager::u_mat4(kShaderUniform::MVP, m_proj * glm::translate(glm::vec3(cur_pos, 0)) * glm::scale(glm::vec3(step * 0.15f, m_size.y * 0.5f, 1)) * glm::translate(glm::vec3(0, .5, 0)) ); m_plane.draw_fill(); */ } kEventResult NodeAnimationTimeline::handle_event(Event* e) { parent::handle_event(e); static int signaled_frame = -1; auto me = static_cast(e); auto ge = static_cast(e); auto update = [&](){ auto loc = me->m_pos - m_pos; m_frame = glm::clamp((int)glm::floor(loc.x / 35.f), 0, Canvas::I->anim_duration() - 1); if (on_frame_changed && signaled_frame != m_frame) on_frame_changed(this, m_frame); signaled_frame = m_frame; }; switch (e->m_type) { case kEventType::MouseDownL: mouse_capture(); m_dragging = true; m_drag_start_frame = m_frame; update(); break; case kEventType::MouseMove: if (m_dragging) update(); break; case kEventType::MouseUpL: m_dragging = false; mouse_release(); break; case kEventType::MouseCancel: m_dragging = false; m_frame = m_drag_start_frame; mouse_release(); break; default: return kEventResult::Available; break; } return kEventResult::Consumed; }