mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Management of inputs in Control, Management of callbacks creator per input in Source, Saving and Loading in Session, Unified renaming of SourceCallbacks, User interface window for creating and editing input mapping from Keyboard and Numerical keypad, with appropriate Settings.
491 lines
11 KiB
C++
491 lines
11 KiB
C++
/*
|
|
* This file is part of vimix - video live mixer
|
|
*
|
|
* **Copyright** (C) 2019-2022 Bruno Herbelin <bruno.herbelin@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
**/
|
|
|
|
#include "defines.h"
|
|
#include "Source.h"
|
|
#include "Visitor.h"
|
|
#include "Log.h"
|
|
|
|
#include "SourceCallback.h"
|
|
|
|
|
|
SourceCallback *SourceCallback::create(CallbackType type)
|
|
{
|
|
SourceCallback *loadedcallback = nullptr;
|
|
|
|
switch (type) {
|
|
case SourceCallback::CALLBACK_ALPHA:
|
|
loadedcallback = new SetAlpha;
|
|
break;
|
|
case SourceCallback::CALLBACK_LOOM:
|
|
loadedcallback = new Loom;
|
|
break;
|
|
case SourceCallback::CALLBACK_DEPTH:
|
|
loadedcallback = new SetDepth;
|
|
break;
|
|
case SourceCallback::CALLBACK_GRAB:
|
|
loadedcallback = new Grab;
|
|
break;
|
|
case SourceCallback::CALLBACK_RESIZE:
|
|
loadedcallback = new Resize;
|
|
break;
|
|
case SourceCallback::CALLBACK_TURN:
|
|
loadedcallback = new Turn;
|
|
break;
|
|
case SourceCallback::CALLBACK_PLAY:
|
|
loadedcallback = new Play;
|
|
break;
|
|
case SourceCallback::CALLBACK_REPLAY:
|
|
loadedcallback = new RePlay;
|
|
break;
|
|
case SourceCallback::CALLBACK_RESETGEO:
|
|
loadedcallback = new ResetGeometry;
|
|
break;
|
|
case SourceCallback::CALLBACK_LOCK:
|
|
loadedcallback = new Lock;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return loadedcallback;
|
|
}
|
|
|
|
SourceCallback::SourceCallback(): active_(true), finished_(false), initialized_(false)
|
|
{
|
|
}
|
|
|
|
void SourceCallback::accept(Visitor& v)
|
|
{
|
|
v.visit(*this);
|
|
}
|
|
|
|
void ResetGeometry::update(Source *s, float)
|
|
{
|
|
s->group(View::GEOMETRY)->scale_ = glm::vec3(1.f);
|
|
s->group(View::GEOMETRY)->rotation_.z = 0;
|
|
s->group(View::GEOMETRY)->crop_ = glm::vec3(1.f);
|
|
s->group(View::GEOMETRY)->translation_ = glm::vec3(0.f);
|
|
s->touch();
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *ResetGeometry::clone() const
|
|
{
|
|
return new ResetGeometry;
|
|
}
|
|
|
|
SetAlpha::SetAlpha() : SourceCallback(), alpha_(0.f)
|
|
{
|
|
pos_ = glm::vec2();
|
|
step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default
|
|
}
|
|
|
|
SetAlpha::SetAlpha(float alpha) : SetAlpha()
|
|
{
|
|
alpha_ = CLAMP(alpha, 0.f, 1.f);
|
|
}
|
|
|
|
void SetAlpha::update(Source *s, float)
|
|
{
|
|
if (s && !s->locked()) {
|
|
// set start position on first run or upon call of reset()
|
|
if (!initialized_){
|
|
// initial position
|
|
pos_ = glm::vec2(s->group(View::MIXING)->translation_);
|
|
// step in direction of source translation if possible
|
|
if ( glm::length(pos_) > DELTA_ALPHA)
|
|
step_ = glm::normalize(pos_);
|
|
|
|
initialized_ = true;
|
|
}
|
|
|
|
// perform operation
|
|
float delta = SourceCore::alphaFromCordinates(pos_.x, pos_.y) - alpha_;
|
|
|
|
// converge to reduce the difference of alpha using dichotomic algorithm
|
|
if ( glm::abs(delta) > DELTA_ALPHA ){
|
|
pos_ += step_ * (delta / 2.f);
|
|
s->group(View::MIXING)->translation_ = glm::vec3(pos_, s->group(View::MIXING)->translation_.z);
|
|
}
|
|
// reached alpha target
|
|
else {
|
|
finished_ = true;
|
|
}
|
|
}
|
|
else
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *SetAlpha::clone() const
|
|
{
|
|
return new SetAlpha(alpha_);
|
|
}
|
|
|
|
SourceCallback *SetAlpha::reverse(Source *s) const
|
|
{
|
|
return new SetAlpha(s->alpha());
|
|
}
|
|
|
|
void SetAlpha::accept(Visitor& v)
|
|
{
|
|
SourceCallback::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
Lock::Lock() : SourceCallback(), lock_(true)
|
|
{
|
|
}
|
|
|
|
Lock::Lock(bool on) : Lock()
|
|
{
|
|
lock_ = on;
|
|
}
|
|
|
|
void Lock::update(Source *s, float)
|
|
{
|
|
if (s)
|
|
s->setLocked(lock_);
|
|
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *Lock::clone() const
|
|
{
|
|
return new Lock(lock_);
|
|
}
|
|
|
|
Loom::Loom() : SourceCallback(), speed_(0), duration_(0), progress_(0.f)
|
|
{
|
|
pos_ = glm::vec2();
|
|
step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default
|
|
}
|
|
|
|
Loom::Loom(float d, float duration) : Loom()
|
|
{
|
|
speed_ = d;
|
|
duration_ = duration;
|
|
pos_ = glm::vec2();
|
|
step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default
|
|
}
|
|
|
|
void Loom::update(Source *s, float dt)
|
|
{
|
|
if (s && !s->locked()) {
|
|
// reset on first run or upon call of reset()
|
|
if (!initialized_){
|
|
// start animation
|
|
progress_ = 0.f;
|
|
// initial position
|
|
pos_ = glm::vec2(s->group(View::MIXING)->translation_);
|
|
// step in direction of source translation if possible
|
|
if ( glm::length(pos_) > DELTA_ALPHA)
|
|
step_ = glm::normalize(pos_);
|
|
|
|
initialized_ = true;
|
|
}
|
|
|
|
// time passed
|
|
progress_ += dt;
|
|
|
|
// move target by speed vector (in the direction of step_, amplitude of speed * time (in second))
|
|
pos_ += step_ * ( speed_ * dt * 0.001f);
|
|
|
|
// apply alpha if valid in range [0 1]
|
|
float alpha = SourceCore::alphaFromCordinates(pos_.x, pos_.y);
|
|
if ( alpha > DELTA_ALPHA && alpha < 1.0 - DELTA_ALPHA )
|
|
s->group(View::MIXING)->translation_ = glm::vec3(pos_, s->group(View::MIXING)->translation_.z);
|
|
|
|
// time-out
|
|
if ( progress_ > duration_ ) {
|
|
// done
|
|
finished_ = true;
|
|
}
|
|
}
|
|
else
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *Loom::clone() const
|
|
{
|
|
return new Loom(speed_, duration_);
|
|
}
|
|
|
|
void Loom::accept(Visitor& v)
|
|
{
|
|
SourceCallback::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
|
|
SetDepth::SetDepth() : SourceCallback(),
|
|
duration_(0), progress_(0.f), start_(0.f), target_(MIN_DEPTH)
|
|
{
|
|
}
|
|
|
|
SetDepth::SetDepth(float target, float duration) : SetDepth()
|
|
{
|
|
target_ = CLAMP(target, MIN_DEPTH, MAX_DEPTH);
|
|
duration_ = duration;
|
|
}
|
|
|
|
void SetDepth::update(Source *s, float dt)
|
|
{
|
|
if (s && !s->locked()) {
|
|
// set start position on first run or upon call of reset()
|
|
if (!initialized_){
|
|
start_ = s->group(View::LAYER)->translation_.z;
|
|
progress_ = 0.f;
|
|
initialized_ = true;
|
|
}
|
|
|
|
// time passed
|
|
progress_ += dt;
|
|
|
|
// perform movement
|
|
if ( ABS(duration_) > 0.f)
|
|
s->group(View::LAYER)->translation_.z = start_ + (progress_/duration_) * (target_ - start_);
|
|
|
|
// time-out
|
|
if ( progress_ > duration_ ) {
|
|
// apply depth to target
|
|
s->group(View::LAYER)->translation_.z = target_;
|
|
// ensure reordering of view
|
|
++View::need_deep_update_;
|
|
// done
|
|
finished_ = true;
|
|
}
|
|
}
|
|
else
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *SetDepth::clone() const
|
|
{
|
|
return new SetDepth(target_, duration_);
|
|
}
|
|
|
|
SourceCallback *SetDepth::reverse(Source *s) const
|
|
{
|
|
return new SetDepth(s->depth(), duration_);
|
|
}
|
|
|
|
void SetDepth::accept(Visitor& v)
|
|
{
|
|
SourceCallback::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
Play::Play() : SourceCallback(), play_(true)
|
|
{
|
|
}
|
|
|
|
Play::Play(bool on) : Play()
|
|
{
|
|
play_ = on;
|
|
}
|
|
|
|
void Play::update(Source *s, float)
|
|
{
|
|
if (s && s->playing() != play_) {
|
|
// call play function
|
|
s->play(play_);
|
|
}
|
|
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *Play::clone() const
|
|
{
|
|
return new Play(play_);
|
|
}
|
|
|
|
SourceCallback *Play::reverse(Source *s) const
|
|
{
|
|
return new Play(s->playing());
|
|
}
|
|
|
|
RePlay::RePlay() : SourceCallback()
|
|
{
|
|
}
|
|
|
|
void RePlay::update(Source *s, float)
|
|
{
|
|
if (s) {
|
|
// call replay function
|
|
s->replay();
|
|
}
|
|
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *RePlay::clone() const
|
|
{
|
|
return new RePlay;
|
|
}
|
|
|
|
Grab::Grab() : SourceCallback(), speed_(glm::vec2(0.f)), duration_(0.f), progress_(0.f)
|
|
{
|
|
}
|
|
|
|
Grab::Grab(float dx, float dy, float duration) : Grab()
|
|
{
|
|
speed_ = glm::vec2(dx,dy);
|
|
duration_ = duration;
|
|
}
|
|
|
|
void Grab::update(Source *s, float dt)
|
|
{
|
|
if (s && !s->locked()) {
|
|
// reset on first run or upon call of reset()
|
|
if (!initialized_){
|
|
// start animation
|
|
progress_ = 0.f;
|
|
// initial position
|
|
start_ = glm::vec2(s->group(View::GEOMETRY)->translation_);
|
|
initialized_ = true;
|
|
}
|
|
|
|
// time passed
|
|
progress_ += dt;
|
|
|
|
// move target by speed vector * time (in second)
|
|
glm::vec2 pos = start_ + speed_ * ( dt * 0.001f);
|
|
s->group(View::GEOMETRY)->translation_ = glm::vec3(pos, s->group(View::GEOMETRY)->translation_.z);
|
|
|
|
// time-out
|
|
if ( progress_ > duration_ ) {
|
|
// done
|
|
finished_ = true;
|
|
}
|
|
}
|
|
else
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *Grab::clone() const
|
|
{
|
|
return new Grab(speed_.x, speed_.y, duration_);
|
|
}
|
|
|
|
void Grab::accept(Visitor& v)
|
|
{
|
|
SourceCallback::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
Resize::Resize() : SourceCallback(), speed_(glm::vec2(0.f)), duration_(0.f), progress_(0.f)
|
|
{
|
|
}
|
|
|
|
Resize::Resize(float dx, float dy, float duration) : Resize()
|
|
{
|
|
speed_ = glm::vec2(dx,dy);
|
|
duration_ = duration;
|
|
}
|
|
|
|
void Resize::update(Source *s, float dt)
|
|
{
|
|
if (s && !s->locked()) {
|
|
// reset on first run or upon call of reset()
|
|
if (!initialized_){
|
|
// start animation
|
|
progress_ = 0.f;
|
|
// initial position
|
|
start_ = glm::vec2(s->group(View::GEOMETRY)->scale_);
|
|
initialized_ = true;
|
|
}
|
|
|
|
// time passed
|
|
progress_ += dt;
|
|
|
|
// move target by speed vector * time (in second)
|
|
glm::vec2 scale = start_ + speed_ * ( dt * 0.001f);
|
|
s->group(View::GEOMETRY)->scale_ = glm::vec3(scale, s->group(View::GEOMETRY)->scale_.z);
|
|
|
|
// time-out
|
|
if ( progress_ > duration_ ) {
|
|
// done
|
|
finished_ = true;
|
|
}
|
|
}
|
|
else
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *Resize::clone() const
|
|
{
|
|
return new Resize(speed_.x, speed_.y, duration_);
|
|
}
|
|
|
|
void Resize::accept(Visitor& v)
|
|
{
|
|
SourceCallback::accept(v);
|
|
v.visit(*this);
|
|
}
|
|
|
|
Turn::Turn() : SourceCallback(), speed_(0.f), duration_(0.f), progress_(0.f)
|
|
{
|
|
}
|
|
|
|
Turn::Turn(float da, float duration) : Turn()
|
|
{
|
|
speed_ = da;
|
|
duration_ = duration;
|
|
}
|
|
|
|
void Turn::update(Source *s, float dt)
|
|
{
|
|
if (s && !s->locked()) {
|
|
// reset on first run or upon call of reset()
|
|
if (!initialized_){
|
|
// start animation
|
|
progress_ = 0.f;
|
|
// initial position
|
|
start_ = s->group(View::GEOMETRY)->rotation_.z;
|
|
initialized_ = true;
|
|
}
|
|
|
|
// calculate amplitude of movement
|
|
progress_ += dt;
|
|
|
|
// perform movement
|
|
s->group(View::GEOMETRY)->rotation_.z = start_ + speed_ * ( dt * -0.001f / M_PI);
|
|
|
|
// timeout
|
|
if ( progress_ > duration_ ) {
|
|
// done
|
|
finished_ = true;
|
|
}
|
|
}
|
|
else
|
|
finished_ = true;
|
|
}
|
|
|
|
SourceCallback *Turn::clone() const
|
|
{
|
|
return new Turn(speed_, duration_);
|
|
}
|
|
|
|
void Turn::accept(Visitor& v)
|
|
{
|
|
SourceCallback::accept(v);
|
|
v.visit(*this);
|
|
}
|