Files
vimix/TransitionView.cpp
2021-10-09 23:40:18 +02:00

421 lines
15 KiB
C++

// Opengl
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_access.hpp>
#include <glm/gtx/vector_angle.hpp>
#include "imgui.h"
#include "ImGuiToolkit.h"
#include <string>
#include <sstream>
#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"
#define POS_TARGET 0.4f
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(POS_TARGET, 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;
// no fading when cross fading
Mixer::manager().session()->setFading( 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)
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<int>( Settings::application.transition.duration / 0.1f );
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), 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<int>( Settings::application.transition.duration );
T = glm::translate(glm::identity<glm::mat4>(), 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 pos_window = Rendering::manager().project(glm::vec3(-0.2f, -0.14f, 0.f), scene.root()->transform_, false);
glm::vec2 pos_play = Rendering::manager().project(glm::vec3(0.f, -0.14f, 0.f), scene.root()->transform_, false);
glm::vec2 pos_open = Rendering::manager().project(glm::vec3(POS_TARGET, -0.14f, 0.f), scene.root()->transform_, false);
ImGui::SetNextWindowPos(ImVec2(pos_window.x, pos_window.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);
// Duration slider (adjusted width)
ImGui::SetNextItemWidth(pos_play.x - pos_window.x - 20.f);
ImGui::SliderFloat("##transitionduration", &Settings::application.transition.duration,
TRANSITION_MIN_DURATION, TRANSITION_MAX_DURATION, "%.1f s");
// Play button just at the end of the timeline
ImGui::SetCursorScreenPos(ImVec2(pos_play.x, pos_play.y+2));
if ( ImGui::Button(ICON_FA_PLAY_CIRCLE) )
play(false);
// centered "Open" button on the target frame
ImGui::SetCursorScreenPos(ImVec2(pos_open.x -80.f, pos_open.y +2.f));
ImGui::SetNextItemWidth(160.f);
if ( ImGui::Button(ICON_FA_FILE_UPLOAD " Open") )
open();
ImGui::PopFont();
ImGui::PopStyleColor(7); // 7 colors
ImGui::End();
}
pos_window = Rendering::manager().project(glm::vec3(-0.535f, -0.14f, 0.f), scene.root()->transform_, false);
ImGui::SetNextWindowPos(ImVec2(pos_window.x, pos_window.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<Node *, glm::vec2> TransitionView::pick(glm::vec2 P)
{
std::pair<Node *, glm::vec2> 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::open()
{
// quick jump over to target (and open)
transition_source_->group(View::TRANSITION)->clearCallbacks();
MoveToCallback *anim = new MoveToCallback(glm::vec3(POS_TARGET, 0.0, 0.0), 180.f);
transition_source_->group(View::TRANSITION)->update_callbacks_.push_back(anim);
}
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 ? POS_TARGET : 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<Node *, glm::vec2>)
{
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 * dt_;
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;
}