mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
447 lines
17 KiB
C++
447 lines
17 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 GUI if there is a transition source...
|
|
if ( transition_source_ ) {
|
|
|
|
// ..and a valid TRANSITION group
|
|
const Group *tg = transition_source_->group(View::TRANSITION);
|
|
if (tg) {
|
|
|
|
// display interface below timeline
|
|
glm::vec2 pos_window = Rendering::manager().project(glm::vec3(-1.2f, -0.10f, 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))
|
|
{
|
|
const glm::vec2 pos_canl = Rendering::manager().project(glm::vec3(-1.0f, -0.15f, 0.f), scene.root()->transform_, false);
|
|
const glm::vec2 pos_tran = Rendering::manager().project(glm::vec3(-0.5f, -0.15f, 0.f), scene.root()->transform_, false);
|
|
const glm::vec2 pos_play = Rendering::manager().project(glm::vec3(0.f, -0.15f, 0.f), scene.root()->transform_, false);
|
|
const glm::vec2 pos_open = Rendering::manager().project(glm::vec3(POS_TARGET, -0.15f, 0.f), scene.root()->transform_, false);
|
|
|
|
// 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);
|
|
|
|
// "Cancel" button at the begining of the timeline, only if transition not started
|
|
if (tg->translation_.x < -1.f + EPSILON) {
|
|
ImGui::SetCursorScreenPos(ImVec2(pos_canl.x -80.f, pos_canl.y +2.f));
|
|
ImGui::SetNextItemWidth(160.f);
|
|
if ( ImGui::Button(ICON_FA_TIMES " Cancel") )
|
|
cancel();
|
|
}
|
|
|
|
// toggle transition mode
|
|
if (!Settings::application.transition.cross_fade) {
|
|
// black background in icon 'transition to black'
|
|
ImGui::SetCursorScreenPos(ImVec2(pos_tran.x - 20.f, pos_tran.y +2.f));
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.f));
|
|
ImGuiToolkit::Icon(19,1);
|
|
ImGui::PopStyleColor();
|
|
}
|
|
ImGui::SetCursorScreenPos(ImVec2(pos_tran.x - 20.f, pos_tran.y +2.f));
|
|
const char *tooltip[2] = {"Transition to black", "Cross fading"};
|
|
ImGuiToolkit::IconToggle(0,2,0,8, &Settings::application.transition.cross_fade, tooltip );
|
|
|
|
|
|
// Duration slider (adjusted width)
|
|
const float width = (pos_play.x - pos_canl.x) / 5.0;
|
|
ImGui::SetCursorScreenPos(ImVec2(pos_play.x - width, pos_play.y +2.f));
|
|
ImGui::SetNextItemWidth( width );
|
|
ImGui::SliderFloat("##transitionduration", &Settings::application.transition.duration,
|
|
TRANSITION_MIN_DURATION, TRANSITION_MAX_DURATION, "%.1f s");
|
|
|
|
// Play-Pause button just at the end of the timeline
|
|
ImGui::SetCursorScreenPos(ImVec2(pos_play.x + 10.f, pos_play.y +2.f));
|
|
if ( ImGui::Button( tg->update_callbacks_.empty() ? ICON_FA_PLAY : ICON_FA_PAUSE) )
|
|
// toggle play but do not open
|
|
play(false);
|
|
|
|
// "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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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()
|
|
{
|
|
if (transition_source_ != nullptr) {
|
|
// 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::cancel()
|
|
{
|
|
if (transition_source_ != nullptr) {
|
|
// cancel any animation
|
|
transition_source_->group(View::TRANSITION)->clearCallbacks();
|
|
// get and detatch the group node from the view workspace
|
|
scene.ws()->detach( transition_source_->group(View::TRANSITION) );
|
|
|
|
// done with transition source
|
|
Mixer::manager().deleteSource(transition_source_);
|
|
transition_source_ = nullptr;
|
|
// quit view
|
|
Mixer::manager().setView(View::MIXING);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|