diff --git a/CMakeLists.txt b/CMakeLists.txt index d50ce55..32e2851 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -292,6 +292,7 @@ set(VMIX_SRCS TextureView.cpp TransitionView.cpp Source.cpp + SourceCallback.cpp SourceList.cpp Session.cpp Selection.cpp diff --git a/LayerView.cpp b/LayerView.cpp index dcfbade..51e92a2 100644 --- a/LayerView.cpp +++ b/LayerView.cpp @@ -138,10 +138,9 @@ void LayerView::draw() float depth_inc = (dsl.back()->depth() - depth) / static_cast(Mixer::selection().size()-1); for (++it; it != dsl.end(); ++it) { depth += depth_inc; - (*it)->setDepth(depth); + (*it)->call( new SetDepth(depth, 80.f) ); } Action::manager().store(std::string("Selection: Layer Distribute")); - ++View::need_deep_update_; } if (ImGui::Selectable( ICON_FA_RULER_HORIZONTAL " Compress" )){ SourceList dsl = depth_sorted(Mixer::selection().getCopy()); @@ -149,20 +148,18 @@ void LayerView::draw() float depth = (*it)->depth(); for (++it; it != dsl.end(); ++it) { depth += LAYER_STEP; - (*it)->setDepth(depth); + (*it)->call( new SetDepth(depth, 80.f) ); } Action::manager().store(std::string("Selection: Layer Compress")); - ++View::need_deep_update_; } if (ImGui::Selectable( ICON_FA_EXCHANGE_ALT " Reverse order" )){ SourceList dsl = depth_sorted(Mixer::selection().getCopy()); SourceList::iterator it = dsl.begin(); SourceList::reverse_iterator rit = dsl.rbegin(); for (; it != dsl.end(); ++it, ++rit) { - (*it)->setDepth((*rit)->depth()); + (*it)->call( new SetDepth((*rit)->depth(), 80.f) ); } Action::manager().store(std::string("Selection: Layer Reverse order")); - ++View::need_deep_update_; } ImGui::PopStyleColor(2); diff --git a/Mixer.cpp b/Mixer.cpp index ddd74ac..ee0b1cd 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -831,14 +831,12 @@ void Mixer::setCurrentSource(Source *s) setCurrentSource( session_->find(s) ); } - Source *Mixer::sourceAtIndex (int index) { SourceList::iterator s = session_->at(index); if (s!=session_->end()) return *s; - else - return nullptr; + return nullptr; } void Mixer::setCurrentIndex(int index) @@ -1143,7 +1141,7 @@ void Mixer::merge(SessionSource *source) SourceList::iterator it = to_be_moved.begin(); for (; it != to_be_moved.end(); ++it) { float scale_depth = (MAX_DEPTH-(*it)->depth()) / (MAX_DEPTH-next_depth); - (*it)->setDepth( (*it)->depth() + scale_depth ); + (*it)->call( new SetDepth( (*it)->depth() + scale_depth ) ); } } } @@ -1155,10 +1153,10 @@ void Mixer::merge(SessionSource *source) renameSource(s); // scale alpha - s->setAlpha( s->alpha() * source->alpha() ); + s->call( new SetAlpha(s->alpha() * source->alpha())); // set depth (proportional to depth of s, adjusted by needed space) - s->setDepth( target_depth + ( (s->depth()-start_depth)/ need_depth) ); + s->call( new SetDepth( target_depth + ( (s->depth()-start_depth)/ need_depth) ) ); // set location // a. transform of node to import diff --git a/MixingView.cpp b/MixingView.cpp index 55f97dd..b15f5ec 100644 --- a/MixingView.cpp +++ b/MixingView.cpp @@ -244,14 +244,14 @@ void MixingView::draw() if (ImGui::Selectable( ICON_FA_CLOUD_SUN " Expand & hide" )){ SourceList::iterator it = Mixer::selection().begin(); for (; it != Mixer::selection().end(); ++it) { - (*it)->setAlpha(0.f); + (*it)->call( new SetAlpha(0.f) ); } Action::manager().store(std::string("Selection: Mixing Expand & hide")); } if (ImGui::Selectable( ICON_FA_SUN " Compress & show" )){ SourceList::iterator it = Mixer::selection().begin(); for (; it != Mixer::selection().end(); ++it) { - (*it)->setAlpha(0.99f); + (*it)->call( new SetAlpha(0.999f) ); } Action::manager().store(std::string("Selection: Mixing Compress & show")); } diff --git a/Source.cpp b/Source.cpp index f87bc32..aefd30f 100644 --- a/Source.cpp +++ b/Source.cpp @@ -537,6 +537,11 @@ void Source::setActive (bool on) groups_[View::LAYER]->visible_ = active_; } +void Source::setActive (float threshold) +{ + Source::setActive( glm::length( glm::vec2(groups_[View::MIXING]->translation_) ) < threshold ); +} + void Source::setLocked (bool on) { locked_ = on; @@ -546,21 +551,27 @@ void Source::setLocked (bool on) // Transfer functions from coordinates to alpha (1 - transparency) -// linear distance -float linear_(float x, float y) { - return 1.f - CLAMP( sqrt( ( x * x ) + ( y * y ) ), 0.f, 1.f ); -} +//// linear distance +//float linear_(float x, float y) { +// return 1.f - CLAMP( sqrt( ( x * x ) + ( y * y ) ), 0.f, 1.f ); +//} -// quadratic distance -float quad_(float x, float y) { - return 1.f - CLAMP( ( x * x ) + ( y * y ), 0.f, 1.f ); -} +//// quadratic distance +//float quad_(float x, float y) { +// return 1.f - CLAMP( ( x * x ) + ( y * y ), 0.f, 1.f ); +//} -// best alpha transfer function: quadratic sinusoidal shape -float sin_quad_(float x, float y) { +//// best alpha transfer function: quadratic sinusoidal shape +//float sin_quad_(float x, float y) { +// float D = sqrt( ( x * x ) + ( y * y ) ); +// return 0.5f + 0.5f * cos( M_PI * CLAMP( D * sqrt(D), 0.f, 1.f ) ); +//// return 0.5f + 0.5f * cos( M_PI * CLAMP( ( ( x * x ) + ( y * y ) ), 0.f, 1.f ) ); +//} + +float SourceCore::alphaFromCordinates(float x, float y) +{ float D = sqrt( ( x * x ) + ( y * y ) ); return 0.5f + 0.5f * cos( M_PI * CLAMP( D * sqrt(D), 0.f, 1.f ) ); -// return 0.5f + 0.5f * cos( M_PI * CLAMP( ( ( x * x ) + ( y * y ) ), 0.f, 1.f ) ); } @@ -569,44 +580,41 @@ float Source::depth() const return group(View::RENDERING)->translation_.z; } -void Source::setDepth(float d) -{ - groups_[View::LAYER]->translation_.z = CLAMP(d, MIN_DEPTH, MAX_DEPTH); - touch(); -} - -float Source::mix_distance() -{ - return glm::length( glm::vec2(groups_[View::MIXING]->translation_) ); -} float Source::alpha() const { return blendingShader()->color.a; } -void Source::setAlpha(float a) +void Source::call(SourceCallback *callback, bool override) { - a = CLAMP(a, 0.f, 1.f); - glm::vec2 dist = glm::vec2(groups_[View::MIXING]->translation_); - glm::vec2 step = glm::normalize(glm::vec2(1.f, 1.f));// step in diagonal by default + if (callback != nullptr) { - // step in direction of source translation if possible - if ( glm::length(dist) > DELTA_ALPHA) - step = glm::normalize(dist); + // lock access to callbacks list + access_callbacks_.lock(); - // converge to reduce the difference of alpha - // using dichotomic algorithm - float delta = sin_quad_(dist.x, dist.y) - a; - while ( glm::abs(delta) > DELTA_ALPHA ){ - dist += step * (delta / 2.f); - delta = sin_quad_(dist.x, dist.y) - a; + // remove similar callbacks if override + if (override) { + for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ) + { + // remove and delete all callbacks of same type + SourceCallback *c = *iter; + if (callback->type() == c->type() ) { + iter = update_callbacks_.erase(iter); + delete c; + } + // iterate + else + ++iter; + } + } + + // add callback to callbacks list + update_callbacks_.push_back(callback); + + // release access to callbacks list + access_callbacks_.unlock(); } - - // apply new mixing coordinates - groups_[View::MIXING]->translation_.x = dist.x; - groups_[View::MIXING]->translation_.y = dist.y; - touch(); } void Source::update(float dt) @@ -614,122 +622,153 @@ void Source::update(float dt) // keep delta-t dt_ = dt; - // update nodes if needed - if (renderbuffer_ && mixingsurface_ && maskbuffer_ && need_update_) + // if update is possible + if (renderbuffer_ && mixingsurface_ && maskbuffer_) { - // ADJUST alpha based on MIXING node - // read position of the mixing node and interpret this as transparency of render output - glm::vec2 dist = glm::vec2(groups_[View::MIXING]->translation_); - // use the sinusoidal transfer function - blendingshader_->color = glm::vec4(1.f, 1.f, 1.f, sin_quad_( dist.x, dist.y )); - mixingshader_->color = blendingshader_->color; - // adjust scale of mixing icon : smaller if not active - groups_[View::MIXING]->scale_ = glm::vec3(MIXING_ICON_SCALE) - ( active_ ? glm::vec3(0.f, 0.f, 0.f) : glm::vec3(0.03f, 0.03f, 0.f) ); - - // MODIFY geometry based on GEOMETRY node - groups_[View::RENDERING]->translation_ = groups_[View::GEOMETRY]->translation_; - groups_[View::RENDERING]->rotation_ = groups_[View::GEOMETRY]->rotation_; - glm::vec3 s = groups_[View::GEOMETRY]->scale_; - // avoid any null scale - s.x = CLAMP_SCALE(s.x); - s.y = CLAMP_SCALE(s.y); - s.z = 1.f; - groups_[View::GEOMETRY]->scale_ = s; - groups_[View::RENDERING]->scale_ = s; - - // MODIFY CROP projection based on GEOMETRY crop - renderbuffer_->setProjectionArea( glm::vec2(groups_[View::GEOMETRY]->crop_) ); - - // Mixing and layer icons scaled based on GEOMETRY crop - mixingsurface_->scale_ = groups_[View::GEOMETRY]->crop_; - mixingsurface_->scale_.x *= renderbuffer_->aspectRatio(); - mixingsurface_->update(dt_); - - // Layers icons are displayed in Perspective (diagonal) - groups_[View::LAYER]->translation_.x = -groups_[View::LAYER]->translation_.z; - groups_[View::LAYER]->translation_.y = groups_[View::LAYER]->translation_.x / LAYER_PERSPECTIVE; - - // Update workspace based on depth, and - // adjust vertical position of icon depending on workspace - if (groups_[View::LAYER]->translation_.x < -LAYER_FOREGROUND) { - groups_[View::LAYER]->translation_.y -= 0.3f; - workspace_ = Source::FOREGROUND; - } - else if (groups_[View::LAYER]->translation_.x < -LAYER_BACKGROUND) { - groups_[View::LAYER]->translation_.y -= 0.15f; - workspace_ = Source::STAGE; - } - else - workspace_ = Source::BACKGROUND; - - // MODIFY depth based on LAYER node - groups_[View::MIXING]->translation_.z = groups_[View::LAYER]->translation_.z; - groups_[View::GEOMETRY]->translation_.z = groups_[View::LAYER]->translation_.z; - groups_[View::RENDERING]->translation_.z = groups_[View::LAYER]->translation_.z; - - // MODIFY texture projection based on APPEARANCE node - // UV to node coordinates - static glm::mat4 UVtoScene = GlmToolkit::transform(glm::vec3(1.f, -1.f, 0.f), - glm::vec3(0.f, 0.f, 0.f), - glm::vec3(-2.f, 2.f, 1.f)); - // Aspect Ratio correction transform : coordinates of Appearance Frame are scaled by render buffer width - glm::mat4 Ar = glm::scale(glm::identity(), glm::vec3(renderbuffer_->aspectRatio(), 1.f, 1.f) ); - // Translation : same as Appearance Frame (modified by Ar) - glm::mat4 Tra = glm::translate(glm::identity(), groups_[View::TEXTURE]->translation_); - // Scaling : inverse scaling (larger UV when smaller Appearance Frame) - glm::vec2 scale = glm::vec2(groups_[View::TEXTURE]->scale_.x,groups_[View::TEXTURE]->scale_.y); - scale = glm::sign(scale) * glm::max( glm::vec2(glm::epsilon()), glm::abs(scale)); - glm::mat4 Sca = glm::scale(glm::identity(), glm::vec3(scale, 1.f)); - // Rotation : same angle than Appearance Frame, inverted axis - glm::mat4 Rot = glm::rotate(glm::identity(), groups_[View::TEXTURE]->rotation_.z, glm::vec3(0.f, 0.f, -1.f) ); - // Combine transformations (non transitive) in this order: - // 1. switch to Scene coordinate system - // 2. Apply the aspect ratio correction - // 3. Apply the translation - // 4. Apply the rotation (centered after translation) - // 5. Revert aspect ration correction - // 6. Apply the Scaling (independent of aspect ratio) - // 7. switch back to UV coordinate system - texturesurface_->shader()->iTransform = glm::inverse(UVtoScene) * glm::inverse(Sca) * glm::inverse(Ar) * Rot * Tra * Ar * UVtoScene; - - // if a mask image was given to be updated - if (mask_need_update_) { - // fill the mask buffer (once) - if (maskbuffer_->fill(maskimage_) ) - mask_need_update_ = false; - } - // otherwise, render the mask buffer - else + // lock access to callbacks list + access_callbacks_.lock(); + // call all callbacks + for (auto iter=update_callbacks_.begin(); iter != update_callbacks_.end(); ) { - // draw mask in mask frame buffer - maskbuffer_->begin(false); - // loopback maskbuffer texture for painting - masksurface_->setTextureIndex(maskbuffer_->texture()); - // fill surface with mask texture - masksurface_->draw(glm::identity(), maskbuffer_->projection()); - maskbuffer_->end(); - } + SourceCallback *callback = *iter; - // set the rendered mask as mask for blending - blendingshader_->mask_texture = maskbuffer_->texture(); + // call update for active callbacks + if (callback->active()) { + callback->update(this, dt); + need_update_ = true; + } - // inform mixing group - if (mixinggroup_) - mixinggroup_->setAction(MixingGroup::ACTION_UPDATE); - - // do not update next frame - need_update_ = false; - } - - if (processingshader_link_.connected() && imageProcessingEnabled()) { - Source *ref_source = processingshader_link_.source(); - if (ref_source!=nullptr) { - if (ref_source->imageProcessingEnabled()) - processingshader_->copy( *ref_source->processingShader() ); + // remove and delete finished callbacks + if (callback->finished()) { + iter = update_callbacks_.erase(iter); + delete callback; + } + // iterate else - processingshader_link_.disconnect(); + ++iter; } + // release access to callbacks list + access_callbacks_.unlock(); + + // update nodes if needed + if (need_update_) + { + // ADJUST alpha based on MIXING node + // read position of the mixing node and interpret this as transparency of render output + glm::vec2 dist = glm::vec2(groups_[View::MIXING]->translation_); + // use the sinusoidal transfer function + blendingshader_->color = glm::vec4(1.f, 1.f, 1.f, SourceCore::alphaFromCordinates( dist.x, dist.y )); + mixingshader_->color = blendingshader_->color; + // adjust scale of mixing icon : smaller if not active + groups_[View::MIXING]->scale_ = glm::vec3(MIXING_ICON_SCALE) - ( active_ ? glm::vec3(0.f, 0.f, 0.f) : glm::vec3(0.03f, 0.03f, 0.f) ); + + // MODIFY geometry based on GEOMETRY node + groups_[View::RENDERING]->translation_ = groups_[View::GEOMETRY]->translation_; + groups_[View::RENDERING]->rotation_ = groups_[View::GEOMETRY]->rotation_; + glm::vec3 s = groups_[View::GEOMETRY]->scale_; + // avoid any null scale + s.x = CLAMP_SCALE(s.x); + s.y = CLAMP_SCALE(s.y); + s.z = 1.f; + groups_[View::GEOMETRY]->scale_ = s; + groups_[View::RENDERING]->scale_ = s; + + // MODIFY CROP projection based on GEOMETRY crop + renderbuffer_->setProjectionArea( glm::vec2(groups_[View::GEOMETRY]->crop_) ); + + // Mixing and layer icons scaled based on GEOMETRY crop + mixingsurface_->scale_ = groups_[View::GEOMETRY]->crop_; + mixingsurface_->scale_.x *= renderbuffer_->aspectRatio(); + mixingsurface_->update(dt_); + + // Layers icons are displayed in Perspective (diagonal) + groups_[View::LAYER]->translation_.x = -groups_[View::LAYER]->translation_.z; + groups_[View::LAYER]->translation_.y = groups_[View::LAYER]->translation_.x / LAYER_PERSPECTIVE; + + // Update workspace based on depth, and + // adjust vertical position of icon depending on workspace + if (groups_[View::LAYER]->translation_.x < -LAYER_FOREGROUND) { + groups_[View::LAYER]->translation_.y -= 0.3f; + workspace_ = Source::FOREGROUND; + } + else if (groups_[View::LAYER]->translation_.x < -LAYER_BACKGROUND) { + groups_[View::LAYER]->translation_.y -= 0.15f; + workspace_ = Source::STAGE; + } + else + workspace_ = Source::BACKGROUND; + + // MODIFY depth based on LAYER node + groups_[View::MIXING]->translation_.z = groups_[View::LAYER]->translation_.z; + groups_[View::GEOMETRY]->translation_.z = groups_[View::LAYER]->translation_.z; + groups_[View::RENDERING]->translation_.z = groups_[View::LAYER]->translation_.z; + + // MODIFY texture projection based on APPEARANCE node + // UV to node coordinates + static glm::mat4 UVtoScene = GlmToolkit::transform(glm::vec3(1.f, -1.f, 0.f), + glm::vec3(0.f, 0.f, 0.f), + glm::vec3(-2.f, 2.f, 1.f)); + // Aspect Ratio correction transform : coordinates of Appearance Frame are scaled by render buffer width + glm::mat4 Ar = glm::scale(glm::identity(), glm::vec3(renderbuffer_->aspectRatio(), 1.f, 1.f) ); + // Translation : same as Appearance Frame (modified by Ar) + glm::mat4 Tra = glm::translate(glm::identity(), groups_[View::TEXTURE]->translation_); + // Scaling : inverse scaling (larger UV when smaller Appearance Frame) + glm::vec2 scale = glm::vec2(groups_[View::TEXTURE]->scale_.x,groups_[View::TEXTURE]->scale_.y); + scale = glm::sign(scale) * glm::max( glm::vec2(glm::epsilon()), glm::abs(scale)); + glm::mat4 Sca = glm::scale(glm::identity(), glm::vec3(scale, 1.f)); + // Rotation : same angle than Appearance Frame, inverted axis + glm::mat4 Rot = glm::rotate(glm::identity(), groups_[View::TEXTURE]->rotation_.z, glm::vec3(0.f, 0.f, -1.f) ); + // Combine transformations (non transitive) in this order: + // 1. switch to Scene coordinate system + // 2. Apply the aspect ratio correction + // 3. Apply the translation + // 4. Apply the rotation (centered after translation) + // 5. Revert aspect ration correction + // 6. Apply the Scaling (independent of aspect ratio) + // 7. switch back to UV coordinate system + texturesurface_->shader()->iTransform = glm::inverse(UVtoScene) * glm::inverse(Sca) * glm::inverse(Ar) * Rot * Tra * Ar * UVtoScene; + + // if a mask image was given to be updated + if (mask_need_update_) { + // fill the mask buffer (once) + if (maskbuffer_->fill(maskimage_) ) + mask_need_update_ = false; + } + // otherwise, render the mask buffer + else + { + // draw mask in mask frame buffer + maskbuffer_->begin(false); + // loopback maskbuffer texture for painting + masksurface_->setTextureIndex(maskbuffer_->texture()); + // fill surface with mask texture + masksurface_->draw(glm::identity(), maskbuffer_->projection()); + maskbuffer_->end(); + } + + // set the rendered mask as mask for blending + blendingshader_->mask_texture = maskbuffer_->texture(); + + // inform mixing group + if (mixinggroup_) + mixinggroup_->setAction(MixingGroup::ACTION_UPDATE); + + // do not update next frame + need_update_ = false; + } + + if (processingshader_link_.connected() && imageProcessingEnabled()) { + Source *ref_source = processingshader_link_.source(); + if (ref_source!=nullptr) { + if (ref_source->imageProcessingEnabled()) + processingshader_->copy( *ref_source->processingShader() ); + else + processingshader_link_.disconnect(); + } + } + } + } FrameBuffer *Source::frame() const diff --git a/Source.h b/Source.h index 2fa5dd1..49820f4 100644 --- a/Source.h +++ b/Source.h @@ -4,9 +4,11 @@ #include #include #include +#include #include #include "View.h" +#include "SourceCallback.h" #define DEFAULT_MIXING_TRANSLATION -1.f, 1.f @@ -23,6 +25,7 @@ #define ICON_SOURCE_CLONE 9, 2 #define ICON_SOURCE 12, 11 +class SourceCallback; class ImageShader; class MaskShader; class ImageProcessingShader; @@ -56,6 +59,9 @@ public: void copy(SourceCore const& other); + // alpha transfer function + static float alphaFromCordinates(float x, float y); + protected: // nodes std::map groups_; @@ -136,9 +142,13 @@ public: // a Source shall be updated before displayed (Mixing, Geometry and Layer) virtual void update (float dt); + // add callback to each update + void call(SourceCallback *callback, bool override = false); + // update mode inline bool active () const { return active_; } virtual void setActive (bool on); + void setActive (float threshold); // lock mode inline bool locked () const { return locked_; } @@ -178,14 +188,9 @@ public: void setMask (FrameBufferImage *img); void storeMask (FrameBufferImage *img = nullptr); - // operations on depth + // get properties float depth () const; - void setDepth (float d); - - // operations on alpha - float mix_distance (); float alpha () const; - void setAlpha (float a); // groups for mixing MixingGroup *mixingGroup() const { return mixinggroup_; } @@ -304,6 +309,8 @@ protected: bool need_update_; float dt_; Workspace workspace_; + std::list update_callbacks_; + std::mutex access_callbacks_; // clones CloneList clones_; diff --git a/SourceCallback.cpp b/SourceCallback.cpp new file mode 100644 index 0000000..96556d5 --- /dev/null +++ b/SourceCallback.cpp @@ -0,0 +1,162 @@ +/* + * This file is part of vimix - video live mixer + * + * **Copyright** (C) 2020-2021 Bruno Herbelin + * + * 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 . +**/ + +#include "defines.h" +#include "Source.h" +#include "Log.h" + +#include "SourceCallback.h" + +SourceCallback::SourceCallback(): active_(true), finished_(false), initialized_(false) +{ +} + +SetAlpha::SetAlpha(float alpha) : SourceCallback(), alpha_(CLAMP(alpha, 0.f, 1.f)) +{ + step_ = glm::normalize(glm::vec2(1.f, 1.f)); // step in diagonal by default +} + +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); + } + // done + else + finished_ = true; + } + else + finished_ = true; +} + +SetDepth::SetDepth(float target, float duration) : SourceCallback(), + duration_(duration), progress_(0.f), start_(0.f), target_(CLAMP(target, MIN_DEPTH, MAX_DEPTH)) +{ +} + +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; + } + + // calculate amplitude of movement + progress_ += dt / duration_; + + // perform movement + s->group(View::LAYER)->translation_.z = start_ + progress_ * (target_ - start_); + + // end of movement + if ( progress_ > 1.f ) { + // 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; +} + + +SetPlay::SetPlay(bool on, float delay) : SourceCallback(), play_(on), delay_(delay), progress_(0.f) +{ +} + +void SetPlay::update(Source *s, float dt) +{ + if (s && s->playing() != play_) { + // reset on first run or upon call of reset() + if (!initialized_){ + progress_ = 0.f; + initialized_ = true; + } + + // increment time count + progress_ += dt; + + // timeout + if ( progress_ > delay_ ) { + // call play function + s->play(play_); + // done + finished_ = true; + } + } + else + finished_ = true; +} + +Translation::Translation(float dx, float dy, float duration) : SourceCallback(), speed_(glm::vec2(dx,dy)), + duration_(duration), progress_(0.f) +{ +} + +void Translation::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; + } + + // calculate amplitude of movement + progress_ += dt / duration_; + + // perform movement + glm::vec2 pos = start_ + speed_ * dt; + s->group(View::GEOMETRY)->translation_ = glm::vec3(pos, s->group(View::GEOMETRY)->translation_.z); + + // timeout + if ( progress_ > 1.f ) { + // done + finished_ = true; + } + } + else + finished_ = true; +} diff --git a/SourceCallback.h b/SourceCallback.h new file mode 100644 index 0000000..30a8490 --- /dev/null +++ b/SourceCallback.h @@ -0,0 +1,88 @@ +#ifndef SOURCECALLBACK_H +#define SOURCECALLBACK_H + +#include + +class Source; + +class SourceCallback +{ +public: + + typedef enum { + CALLBACK_GENERIC = 0, + CALLBACK_ALPHA, + CALLBACK_DEPTH, + CALLBACK_PLAY, + CALLBACK_TRANSLATION + } CallbackType; + + SourceCallback(); + virtual ~SourceCallback() {} + + virtual void update(Source *, float) = 0; + virtual CallbackType type () { return CALLBACK_GENERIC; } + + inline bool finished() const { return finished_; } + inline bool active() const { return active_; } + inline void reset() { initialized_ = false; } + +protected: + bool active_; + bool finished_; + bool initialized_; +}; + +class SetAlpha : public SourceCallback +{ + float alpha_; + glm::vec2 pos_; + glm::vec2 step_; + +public: + SetAlpha(float alpha); + void update(Source *s, float) override; + CallbackType type () override { return CALLBACK_ALPHA; } +}; + +class SetDepth : public SourceCallback +{ + float duration_; + float progress_; + float start_; + float target_; + +public: + SetDepth(float depth, float duration = 0.f); + void update(Source *s, float dt) override; + CallbackType type () override { return CALLBACK_DEPTH; } +}; + +class SetPlay : public SourceCallback +{ + bool play_; + float delay_; + float progress_; + +public: + SetPlay(bool on, float delay = 0.f); + void update(Source *s, float) override; + CallbackType type () override { return CALLBACK_PLAY; } +}; + +class Translation : public SourceCallback +{ + glm::vec2 speed_; + glm::vec2 start_; + glm::vec2 target_; + float duration_; + float progress_; + +public: + Translation(float dx, float dy, float duration = 0.f); + void update(Source *s, float) override; + CallbackType type () override { return CALLBACK_TRANSLATION; } +}; + + +#endif // SOURCECALLBACK_H diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 1f887fe..f682c69 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -1867,7 +1867,7 @@ void UserInterface::RenderMetrics(bool *p_open, int* p_corner, int *p_mode) float v = s->alpha(); ImGui::SetNextItemWidth(rightalign); if ( ImGui::DragFloat("Alpha", &v, 0.01f, 0.f, 1.f) ) - s->setAlpha(v); + s->call(new SetAlpha(v)); if ( ImGui::IsItemDeactivatedAfterEdit() ) { info << "Alpha " << std::fixed << std::setprecision(3) << v; Action::manager().store(info.str()); @@ -4224,6 +4224,9 @@ void Navigator::RenderSourcePannel(Source *s) // const char *tooltip[2] = {"Pin pannel\nCurrent: double-clic on source", "Un-pin Pannel\nCurrent: single-clic on source"}; // ImGuiToolkit::IconToggle(5,2,4,2, &Settings::application.pannel_stick, tooltip ); + ImGui::SetCursorPos(ImVec2(pannel_width_ - 35.f, 15.f)); + ImGui::Text("#%d", Mixer::manager().indexCurrentSource()); + std::string sname = s->name(); ImGui::SetCursorPosY(width_); ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);