// Opengl #include #include #include #include #include #include "imgui.h" #include "ImGuiToolkit.h" #include #include #include "Mixer.h" #include "defines.h" #include "Settings.h" #include "SessionSource.h" #include "DrawVisitor.h" #include "Decorations.h" #include "UserInterfaceManager.h" #include "Log.h" #include "TransitionView.h" TransitionView::TransitionView() : View(TRANSITION), transition_source_(nullptr) { // read default settings if ( Settings::application.views[mode_].name.empty() ) { // no settings found: store application default Settings::application.views[mode_].name = "Transition"; scene.root()->scale_ = glm::vec3(TRANSITION_DEFAULT_SCALE, TRANSITION_DEFAULT_SCALE, 1.0f); scene.root()->translation_ = glm::vec3(1.5f, 0.f, 0.0f); saveSettings(); } else restoreSettings(); // Geometry Scene background gradient_ = new Switch; gradient_->attach(new ImageSurface("images/gradient_0_cross_linear.png")); gradient_->attach(new ImageSurface("images/gradient_1_black_linear.png")); gradient_->attach(new ImageSurface("images/gradient_2_cross_quad.png")); gradient_->attach(new ImageSurface("images/gradient_3_black_quad.png")); gradient_->scale_ = glm::vec3(0.501f, 0.006f, 1.f); gradient_->translation_ = glm::vec3(-0.5f, -0.005f, -0.01f); scene.fg()->attach(gradient_); mark_1s_ = new Mesh("mesh/h_mark.ply"); mark_1s_->translation_ = glm::vec3(-1.f, -0.01f, 0.0f); mark_1s_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f ); scene.fg()->attach(mark_1s_); mark_100ms_ = new Mesh("mesh/h_mark.ply"); mark_100ms_->translation_ = glm::vec3(-1.f, -0.01f, 0.0f); mark_100ms_->scale_ = glm::vec3(0.5f, 0.5f, 0.0f); mark_100ms_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f ); scene.fg()->attach(mark_100ms_); // move the whole forground below the icons scene.fg()->translation_ = glm::vec3(0.f, -0.11f, 0.0f); output_surface_ = new Surface; scene.bg()->attach(output_surface_); Frame *border = new Frame(Frame::ROUND, Frame::THIN, Frame::GLOW); border->color = glm::vec4( COLOR_FRAME, 1.0f ); scene.bg()->attach(border); scene.bg()->scale_ = glm::vec3(0.1f, 0.1f, 1.f); scene.bg()->translation_ = glm::vec3(0.4f, 0.f, 0.0f); } void TransitionView::update(float dt) { // update scene View::update(dt); // a more complete update is requested if (View::need_deep_update_ > 0) { // update rendering of render frame FrameBuffer *output = Mixer::manager().session()->frame(); if (output){ float aspect_ratio = output->aspectRatio(); for (NodeSet::iterator node = scene.bg()->begin(); node != scene.bg()->end(); node++) { (*node)->scale_.x = aspect_ratio; } output_surface_->setTextureIndex( output->texture() ); } } // Update transition source if ( transition_source_ != nullptr) { float d = transition_source_->group(View::TRANSITION)->translation_.x; // Transfer this movement to changes in mixing // cross fading if ( Settings::application.transition.cross_fade ) { float f = 0.f; // change alpha of session: if (Settings::application.transition.profile == 0) // linear => identical coordinates in Mixing View f = d; else { // quadratic => square coordinates in Mixing View f = (d+1.f)*(d+1.f) -1.f; } transition_source_->group(View::MIXING)->translation_.x = CLAMP(f, -1.f, 0.f); transition_source_->group(View::MIXING)->translation_.y = 0.f; } // fade to black else { // change alpha of session ; hidden before -0.5, visible after transition_source_->group(View::MIXING)->translation_.x = d < -0.5f ? -1.f : 0.f; transition_source_->group(View::MIXING)->translation_.y = 0.f; // fade to black at 50% : fade-out [-1.0 -0.5], fade-in [-0.5 0.0] float f = 0.f; if (Settings::application.transition.profile == 0) f = ABS(2.f * d + 1.f); // linear else { f = ( 2.f * d + 1.f); // quadratic f *= f; } Mixer::manager().session()->setFading( 1.f - f ); } // request update transition_source_->touch(); if (d > 0.2f && Settings::application.transition.auto_open) Mixer::manager().setView(View::MIXING); } } void TransitionView::draw() { // update the GUI depending on changes in settings gradient_->setActive( 2*Settings::application.transition.profile + (Settings::application.transition.cross_fade ? 0 : 1) ); // draw scene of this view View::draw(); // 100ms tic marks int n = static_cast( Settings::application.transition.duration / 0.1f ); glm::mat4 T = glm::translate(glm::identity(), glm::vec3( 1.f / n, 0.f, 0.f)); DrawVisitor dv(mark_100ms_, Rendering::manager().Projection()); dv.loop(n+1, T); scene.accept(dv); // 1s tic marks int N = static_cast( Settings::application.transition.duration ); T = glm::translate(glm::identity(), glm::vec3( 10.f / n, 0.f, 0.f)); DrawVisitor dv2(mark_1s_, Rendering::manager().Projection()); dv2.loop(N+1, T); scene.accept(dv2); // display interface duration glm::vec2 P = Rendering::manager().project(glm::vec3(-0.17f, -0.14f, 0.f), scene.root()->transform_, false); ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always); if (ImGui::Begin("##Transition", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus)) { // style grey ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.27f, 0.27f, 0.27f, 0.55f)); ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.27f, 0.27f, 0.27f, 0.79f)); ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.27f, 0.27f, 0.27f, 0.7f)); ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.15f, 0.15f, 0.15f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.10f, 0.10f, 0.10f, 1.00f)); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.27f, 0.27f, 0.27f, 0.55f)); // 7 colors ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); ImGui::SetNextItemWidth(180.f); ImGui::SliderFloat("##transitionduration", &Settings::application.transition.duration, TRANSITION_MIN_DURATION, TRANSITION_MAX_DURATION, "%.1f s"); ImGui::SameLine(); if ( ImGui::Button(ICON_FA_STEP_FORWARD) ) play(false); ImGui::PopFont(); ImGui::PopStyleColor(7); // 7 colors ImGui::End(); } P = Rendering::manager().project(glm::vec3(-0.535f, -0.14f, 0.f), scene.root()->transform_, false); ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always); if (ImGui::Begin("##TransitionType", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus)) { ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); // black background in icon 'transition to black' if (!Settings::application.transition.cross_fade) { ImVec2 draw_pos = ImGui::GetCursorScreenPos(); ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.f)); ImGuiToolkit::Icon(19,1); ImGui::PopStyleColor(); ImGui::SetCursorScreenPos(draw_pos); } // toggle transition mode const char *tooltip[2] = {"Transition to black", "Cross fading"}; ImGuiToolkit::IconToggle(0,2,0,8, &Settings::application.transition.cross_fade, tooltip ); ImGui::PopFont(); ImGui::End(); } } bool TransitionView::canSelect(Source *s) { return ( s!=nullptr && s == transition_source_); } void TransitionView::attach(SessionFileSource *ts) { // store source for later (detatch & interaction) transition_source_ = ts; if ( transition_source_ != nullptr) { // insert in scene Group *tg = transition_source_->group(View::TRANSITION); tg->visible_ = true; scene.ws()->attach(tg); // in fade to black transition, start transition from current fading value if ( !Settings::application.transition.cross_fade) { // reverse calculate x position to match actual fading of session float d = 0.f; if (Settings::application.transition.profile == 0) d = -1.f + 0.5f * Mixer::manager().session()->fading(); // linear else { d = -1.f - 0.5f * ( sqrt(1.f - Mixer::manager().session()->fading()) - 1.f); // quadratic } transition_source_->group(View::TRANSITION)->translation_.x = d; } } } Session *TransitionView::detach() { // by default, nothing to return Session *ret = nullptr; if ( transition_source_ != nullptr) { // get and detatch the group node from the view workspace Group *tg = transition_source_->group(View::TRANSITION); scene.ws()->detach( tg ); // test if the icon of the transition source is "Ready" if ( tg->translation_.x > 0.f ) // detatch the session and return it ret = transition_source_->detach(); else // not detached: make current Mixer::manager().setCurrentSource(transition_source_); // done with transition transition_source_ = nullptr; } return ret; } void TransitionView::zoom (float factor) { if (transition_source_ != nullptr) { float d = transition_source_->group(View::TRANSITION)->translation_.x; d += 0.1f * factor; transition_source_->group(View::TRANSITION)->translation_.x = CLAMP(d, -1.f, 0.f); } } std::pair TransitionView::pick(glm::vec2 P) { std::pair pick = View::pick(P); if (transition_source_ != nullptr) { // start animation when clic on target if (pick.first == output_surface_) play(true); // otherwise cancel animation else transition_source_->group(View::TRANSITION)->clearCallbacks(); } return pick; } void TransitionView::play(bool open) { if (transition_source_ != nullptr) { // toggle play/stop with button if (!transition_source_->group(View::TRANSITION)->update_callbacks_.empty() && !open) { // just cancel previous animation transition_source_->group(View::TRANSITION)->clearCallbacks(); return; } // else cancel previous animation and start new one transition_source_->group(View::TRANSITION)->clearCallbacks(); // if want to open session after play, target movement till end position, otherwise stop at 0 float target_x = open ? 0.4f : 0.f; // calculate how far to reach target float time = CLAMP(- transition_source_->group(View::TRANSITION)->translation_.x, 0.f, 1.f); // extra distance to reach transition if want to open time += open ? 0.2f : 0.f; // calculate remaining time on the total duration, in ms time *= Settings::application.transition.duration * 1000.f; // if remaining time is more than 50ms if (time > 50.f) { // start animation MoveToCallback *anim = new MoveToCallback(glm::vec3(target_x, 0.0, 0.0), time); transition_source_->group(View::TRANSITION)->update_callbacks_.push_back(anim); } // otherwise finish animation else transition_source_->group(View::TRANSITION)->translation_.x = target_x; } } View::Cursor TransitionView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair) { if (!s) return Cursor(); // unproject glm::vec3 gl_Position_from = Rendering::manager().unProject(from, scene.root()->transform_); glm::vec3 gl_Position_to = Rendering::manager().unProject(to, scene.root()->transform_); // compute delta translation float d = s->stored_status_->translation_.x + gl_Position_to.x - gl_Position_from.x; std::ostringstream info; if (d > 0.2) { s->group(mode_)->translation_.x = 0.4; info << "Open session"; } else { s->group(mode_)->translation_.x = CLAMP(d, -1.f, 0.f); info << "Transition " << int( 100.f * (1.f + s->group(View::TRANSITION)->translation_.x)) << "%"; } return Cursor(Cursor_ResizeEW, info.str() ); } void TransitionView::arrow (glm::vec2 movement) { Source *s = Mixer::manager().currentSource(); if (s) { glm::vec3 gl_Position_from = Rendering::manager().unProject(glm::vec2(0.f), scene.root()->transform_); glm::vec3 gl_Position_to = Rendering::manager().unProject(movement, scene.root()->transform_); glm::vec3 gl_delta = gl_Position_to - gl_Position_from; float d = s->group(mode_)->translation_.x + gl_delta.x * ARROWS_MOVEMENT_FACTOR; s->group(mode_)->translation_.x = CLAMP(d, -1.f, 0.f); // request update s->touch(); } } View::Cursor TransitionView::drag (glm::vec2 from, glm::vec2 to) { Cursor ret = View::drag(from, to); // Clamp translation to acceptable area scene.root()->translation_ = glm::clamp(scene.root()->translation_, glm::vec3(1.f, -1.7f, 0.f), glm::vec3(2.f, 1.7f, 0.f)); return ret; }