diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index 19f6306..16ca773 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -471,7 +471,7 @@ void ImGuiVisitor::visit (MediaSource& s) ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) ); } -void ImGuiVisitor::visit (SessionSource& s) +void ImGuiVisitor::visit (SessionFileSource& s) { if (s.session() == nullptr) return; @@ -501,6 +501,21 @@ void ImGuiVisitor::visit (SessionSource& s) ImGuiToolkit::ButtonOpenUrl( SystemToolkit::path_filename(s.path()).c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) ); } +void ImGuiVisitor::visit (SessionGroupSource& s) +{ + if (s.session() == nullptr) + return; + + ImGuiToolkit::Icon(s.icon().x, s.icon().y); + ImGui::SameLine(0, 10); + ImGui::Text("Group"); + + if ( ImGui::Button( ICON_FA_FILE_EXPORT " Ungroup", ImVec2(IMGUI_RIGHT_ALIGN, 0)) ){ +// Mixer::manager().import( &s ); + } + +} + void ImGuiVisitor::visit (RenderSource& s) { ImGuiToolkit::Icon(s.icon().x, s.icon().y); diff --git a/ImGuiVisitor.h b/ImGuiVisitor.h index d1ee8ad..06f2a76 100644 --- a/ImGuiVisitor.h +++ b/ImGuiVisitor.h @@ -23,7 +23,8 @@ public: void visit (ImageProcessingShader& n) override; void visit (Source& s) override; void visit (MediaSource& s) override; - void visit (SessionSource& s) override; + void visit (SessionFileSource& s) override; + void visit (SessionGroupSource& s) override; void visit (RenderSource& s) override; void visit (CloneSource& s) override; void visit (PatternSource& s) override; diff --git a/Mixer.cpp b/Mixer.cpp index 827f95d..4692c14 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -35,7 +35,7 @@ using namespace tinyxml2; #define THREADED_LOADING static std::vector< std::future > sessionLoaders_; static std::vector< std::future > sessionImporters_; -static std::vector< SessionSource * > sessionSourceToImport_; +static std::vector< SessionFileSource * > sessionSourceToImport_; const std::chrono::milliseconds timeout_ = std::chrono::milliseconds(4); @@ -168,7 +168,7 @@ void Mixer::update() // if there is a session source to import if (!sessionSourceToImport_.empty()) { // get the session source to be imported - SessionSource *source = sessionSourceToImport_.back(); + SessionFileSource *source = sessionSourceToImport_.back(); // merge the session inside this session source merge( source ); // important: delete the sessionsource itself @@ -260,7 +260,7 @@ Source * Mixer::createSourceFile(const std::string &path) if ( ext == "mix" ) { // create a session source - SessionSource *ss = new SessionSource; + SessionFileSource *ss = new SessionFileSource; ss->load(path); s = ss; } @@ -351,6 +351,20 @@ Source * Mixer::createSourceNetwork(const std::string &nameconnection) return s; } + +Source * Mixer::createSourceGroup() +{ + SessionGroupSource *s = new SessionGroupSource; + + s->setResolution( Mixer::manager().session()->frame()->resolution() ); + + + // propose a new name + s->setName("Group"); + + return s; +} + Source * Mixer::createSourceClone(const std::string &namesource) { // ready to create a source @@ -622,7 +636,49 @@ void Mixer::deleteSelection() } // empty the selection while ( !selection().empty() ) - deleteSource( selection().front()); // this also remove element from selection() + deleteSource( selection().front() ); // this also remove element from selection() + +} + +void Mixer::groupSelection() +{ + float d = 2.f; + + SessionGroupSource *group = new SessionGroupSource; + group->setResolution( Mixer::manager().session()->frame()->resolution() ); + + // empty the selection + while ( !selection().empty() ) { + + Source *s = selection().front(); + d = s->depth(); + + // import source into group + if ( group->import(s) ) { + // detach & remove element from selection() + detach (s); + // remove source from session + session_->removeSource(s); + } + else + selection().pop_front(); + + } + + // set depth at given location + group->group(View::LAYER)->translation_.z = d; + + // scale alpha + group->setAlpha( 1.f ); + + // Add source to Session + session_->addSource(group); + + // Attach source to Mixer + attach(group); + + // avoid name duplicates + renameSource(group, "group"); } @@ -905,7 +961,7 @@ void Mixer::open(const std::string& filename) Log::Info("\nStarting transition to session %s", filename.c_str()); // create special SessionSource to be used for the smooth transition - SessionSource *ts = new SessionSource; + SessionFileSource *ts = new SessionFileSource; // open filename if specified if (!filename.empty()) ts->load(filename); @@ -936,7 +992,7 @@ void Mixer::import(const std::string& filename) #endif } -void Mixer::import(SessionSource *source) +void Mixer::import(SessionFileSource *source) { sessionSourceToImport_.push_back( source ); } @@ -971,7 +1027,7 @@ void Mixer::merge(Session *session) current_view_->update(0.f); } -void Mixer::merge(SessionSource *source) +void Mixer::merge(SessionFileSource *source) { if ( source == nullptr ) { Log::Warning("Failed to import Session Source."); @@ -1078,7 +1134,7 @@ void Mixer::close() if (Settings::application.smooth_transition) { // create empty SessionSource to be used for the smooth transition - SessionSource *ts = new SessionSource; + SessionFileSource *ts = new SessionFileSource; // insert source and switch to transition view insertSource(ts, View::TRANSITION); diff --git a/Mixer.h b/Mixer.h index 5bb9d81..f972d26 100644 --- a/Mixer.h +++ b/Mixer.h @@ -43,6 +43,7 @@ public: Source * createSourcePattern(uint pattern, glm::ivec2 res); Source * createSourceDevice (const std::string &namedevice); Source * createSourceNetwork(const std::string &nameconnection); + Source * createSourceGroup (); // operations on sources void addSource (Source *s); @@ -52,6 +53,7 @@ public: void detach (Source *s); void deselect (Source *s); void deleteSelection(); + void groupSelection(); // current source Source * currentSource (); @@ -75,8 +77,8 @@ public: View *view (View::Mode m = View::INVALID); void setView (View::Mode m); - void conceal(Source *s); - void uncover(Source *s); + void conceal (Source *s); + void uncover (Source *s); bool concealed(Source *s); // manipulate, load and save sessions @@ -86,9 +88,9 @@ public: void saveas (const std::string& filename); void load (const std::string& filename); void import (const std::string& filename); - void import (SessionSource *source); + void import (SessionFileSource *source); void merge (Session *session); - void merge (SessionSource *source); + void merge (SessionFileSource *source); void set (Session *session); // operations depending on transition mode @@ -108,8 +110,8 @@ protected: SourceList candidate_sources_; SourceList stash_; - void insertSource(Source *s, View::Mode m = View::INVALID); - bool replaceSource(Source *from, Source *to); + void insertSource (Source *s, View::Mode m = View::INVALID); + bool replaceSource (Source *from, Source *to); bool recreateSource(Source *s); void setCurrentSource(SourceList::iterator it); diff --git a/Session.cpp b/Session.cpp index 8370b90..afe36fd 100644 --- a/Session.cpp +++ b/Session.cpp @@ -196,9 +196,9 @@ Source *Session::popSource() return s; } -void Session::setResolution(glm::vec3 resolution) +void Session::setResolution(glm::vec3 resolution, bool useAlpha) { - render_.setResolution(resolution); + render_.setResolution( resolution, useAlpha ); config_[View::RENDERING]->scale_ = resolution; } @@ -240,6 +240,11 @@ SourceList::iterator Session::find(Node *node) return std::find_if(sources_.begin(), sources_.end(), Source::hasNode(node)); } +SourceList::iterator Session::find(float depth_from, float depth_to) +{ + return std::find_if(sources_.begin(), sources_.end(), Source::hasDepth(depth_from, depth_to)); +} + uint Session::numSource() const { return sources_.size(); diff --git a/Session.h b/Session.h index 54a6479..4451710 100644 --- a/Session.h +++ b/Session.h @@ -38,6 +38,7 @@ public: SourceList::iterator find (Source *s); SourceList::iterator find (std::string name); SourceList::iterator find (Node *node); + SourceList::iterator find (float depth_from, float depth_to); SourceList::iterator find (uint64_t id); std::list getIdList() const; @@ -60,7 +61,7 @@ public: inline FrameBuffer *frame () const { return render_.frame(); } // configure rendering resolution - void setResolution(glm::vec3 resolution); + void setResolution(glm::vec3 resolution, bool useAlpha = false); // manipulate fading of output void setFading(float f, bool forcenow = false); diff --git a/SessionCreator.cpp b/SessionCreator.cpp index ef22772..69c5aed 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -142,7 +142,10 @@ void SessionLoader::load(XMLElement *sessionNode) load_source = new MediaSource; } else if ( std::string(pType) == "SessionSource") { - load_source = new SessionSource; + load_source = new SessionFileSource; + } + else if ( std::string(pType) == "GroupSource") { + load_source = new SessionGroupSource; } else if ( std::string(pType) == "RenderSource") { load_source = new RenderSource; @@ -245,7 +248,10 @@ Source *SessionLoader::createSource(tinyxml2::XMLElement *sourceNode, bool clone load_source = new MediaSource; } else if ( std::string(pType) == "SessionSource") { - load_source = new SessionSource; + load_source = new SessionFileSource; + } + else if ( std::string(pType) == "GroupSource") { + load_source = new SessionGroupSource; } else if ( std::string(pType) == "RenderSource") { load_source = new RenderSource; @@ -522,7 +528,7 @@ void SessionLoader::visit (MediaSource& s) s.mediaplayer()->accept(*this); } -void SessionLoader::visit (SessionSource& s) +void SessionLoader::visit (SessionFileSource& s) { // set uri XMLElement* pathNode = xmlCurrent_->FirstChildElement("path"); @@ -535,6 +541,21 @@ void SessionLoader::visit (SessionSource& s) } +void SessionLoader::visit (SessionGroupSource& s) +{ + // set resolution from host session + s.setResolution( session_->config(View::RENDERING)->scale_ ); + + // get the inside session + XMLElement* sessionGroupNode = xmlCurrent_->FirstChildElement("Session"); + if (sessionGroupNode) { + // load session inside group + SessionLoader grouploader( s.session() ); + grouploader.load( sessionGroupNode ); + } + +} + void SessionLoader::visit (RenderSource& s) { s.setSession( session_ ); diff --git a/SessionCreator.h b/SessionCreator.h index ed5b032..ded2480 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -47,7 +47,8 @@ public: // Sources void visit (Source& s) override; void visit (MediaSource& s) override; - void visit (SessionSource& s) override; + void visit (SessionFileSource& s) override; + void visit (SessionGroupSource& s) override; void visit (RenderSource& s) override; void visit (PatternSource& s) override; void visit (DeviceSource& s) override; diff --git a/SessionSource.cpp b/SessionSource.cpp index acef465..11ad108 100644 --- a/SessionSource.cpp +++ b/SessionSource.cpp @@ -17,7 +17,81 @@ #include "Mixer.h" -SessionSource::SessionSource() : Source(), path_("") +SessionSource::SessionSource() : Source() +{ + failed_ = false; + session_ = new Session; +} + +SessionSource::~SessionSource() +{ + // delete session + if (session_) + delete session_; +} + +Session *SessionSource::detach() +{ + // remember pointer to give away + Session *giveaway = session_; + + // work on a new session + session_ = new Session; + + // make disabled + initialized_ = false; + + // ask to delete me + failed_ = true; + + // lost ref to previous session: to be deleted elsewhere... + return giveaway; +} + +bool SessionSource::failed() const +{ + return failed_; +} + +uint SessionSource::texture() const +{ + if (session_ && session_->frame()) + return session_->frame()->texture(); + else + return Resource::getTextureBlack(); +} + +void SessionSource::setActive (bool on) +{ + Source::setActive(on); + + // change status of session (recursive change of internal sources) + if (session_ != nullptr) + session_->setActive(active_); +} + +void SessionSource::update(float dt) +{ + if (session_ == nullptr) + return; + + // update content + if (active_) + session_->update(dt); + + // delete a source which failed + if (session_->failedSource() != nullptr) { + session_->deleteSource(session_->failedSource()); + // fail session if all sources failed + if ( session_->numSource() < 1) + failed_ = true; + } + + Source::update(dt); +} + + +SessionFileSource::SessionFileSource() : SessionSource(), path_("") { // specific node for transition view groups_[View::TRANSITION]->visible_ = false; @@ -51,19 +125,10 @@ SessionSource::SessionSource() : Source(), path_("") symbol_ = new Symbol(Symbol::SESSION, glm::vec3(0.75f, 0.75f, 0.01f)); symbol_->scale_.y = 1.5f; - failed_ = false; wait_for_sources_ = false; - session_ = new Session; } -SessionSource::~SessionSource() -{ - // delete session - if (session_) - delete session_; -} - -void SessionSource::load(const std::string &p) +void SessionFileSource::load(const std::string &p) { path_ = p; @@ -85,37 +150,7 @@ void SessionSource::load(const std::string &p) } } -Session *SessionSource::detach() -{ - // remember pointer to give away - Session *giveaway = session_; - - // work on a new session - session_ = new Session; - - // make disabled - initialized_ = false; - - // ask to delete me - failed_ = true; - - // lost ref to previous session: to be deleted elsewhere... - return giveaway; -} - -bool SessionSource::failed() const -{ - return failed_; -} - -uint SessionSource::texture() const -{ - if (session_ == nullptr) - return Resource::getTextureBlack(); - return session_->frame()->texture(); -} - -void SessionSource::init() +void SessionFileSource::init() { // init is first about getting the loaded session if (session_ == nullptr) { @@ -176,7 +211,7 @@ void SessionSource::init() wait_for_sources_ = true; else { initialized_ = true; - Log::Info("New Session created %d x %d.", session_->frame()->width(), session()->frame()->height()); + Log::Info("New Session created (%d x %d).", renderbuffer->width(), renderbuffer->height()); } } } @@ -192,41 +227,66 @@ void SessionSource::init() } } -void SessionSource::setActive (bool on) -{ - Source::setActive(on); - - // change status of session (recursive change of internal sources) - if (session_ != nullptr) - session_->setActive(active_); -} - - -void SessionSource::update(float dt) -{ - if (session_ == nullptr) - return; - - // update content - if (active_) - session_->update(dt); - - // delete a source which failed - if (session_->failedSource() != nullptr) { - session_->deleteSource(session_->failedSource()); - // fail session if all sources failed - if ( session_->numSource() < 1) - failed_ = true; - } - - Source::update(dt); -} - - -void SessionSource::accept(Visitor& v) +void SessionFileSource::accept(Visitor& v) { Source::accept(v); -// if (!failed()) + if (!failed()) + v.visit(*this); +} + + +SessionGroupSource::SessionGroupSource() : SessionSource(), resolution_(glm::vec3(0.f)) +{ + // set symbol TODO Symbol::GROUP + symbol_ = new Symbol(Symbol::GROUP, glm::vec3(0.75f, 0.75f, 0.01f)); + symbol_->scale_.y = 1.5f; +} + +void SessionGroupSource::init() +{ + if ( resolution_.x > 0.f && resolution_.y > 0.f ) { + + session_->setResolution( resolution_, true ); + + // update to draw framebuffer + session_->update( dt_ ); + + // get the texture index from framebuffer of session, apply it to the surface + texturesurface_->setTextureIndex( session_->frame()->texture() ); + + // create Frame buffer matching size of session + FrameBuffer *renderbuffer = new FrameBuffer( session_->frame()->resolution(), true ); + + // set the renderbuffer of the source and attach rendering nodes + attach(renderbuffer); + + // deep update to reorder + View::need_deep_update_++; + + // done init + initialized_ = true; + Log::Info("Source Group (%d x %d).", int(renderbuffer->resolution().x), int(renderbuffer->resolution().y) ); + } +} + +bool SessionGroupSource::import(Source *source) +{ + bool ret = false; + + if ( session_ ) + { + SourceList::iterator its = session_->addSource(source); + if (its != session_->end()) + ret = true; + } + + return ret; +} + +void SessionGroupSource::accept(Visitor& v) +{ + Source::accept(v); + if (!failed()) v.visit(*this); } diff --git a/SessionSource.h b/SessionSource.h index 44f33ec..c51b2b1 100644 --- a/SessionSource.h +++ b/SessionSource.h @@ -9,22 +9,35 @@ class SessionSource : public Source { public: SessionSource(); - ~SessionSource(); + virtual ~SessionSource(); // implementation of source API void update (float dt) override; void setActive (bool on) override; - bool failed() const override; - uint texture() const override; - void accept (Visitor& v) override; + bool failed () const override; + uint texture () const override; - // Session Source specific interface - void load(const std::string &p = ""); Session *detach(); - - inline std::string path() const { return path_; } inline Session *session() const { return session_; } +protected: + + Session *session_; + std::atomic failed_; +}; + +class SessionFileSource : public SessionSource +{ +public: + SessionFileSource(); + + // implementation of source API + void accept (Visitor& v) override; + + // SessionFile Source specific interface + void load(const std::string &p = ""); + + inline std::string path() const { return path_; } glm::ivec2 icon() const override { return glm::ivec2(2, 16); } protected: @@ -32,13 +45,36 @@ protected: void init() override; std::string path_; - Session *session_; - - std::atomic failed_; std::atomic wait_for_sources_; std::future sessionLoader_; }; +class SessionGroupSource : public SessionSource +{ +public: + SessionGroupSource(); + + // implementation of source API + void accept (Visitor& v) override; + + // SessionGroup Source specific interface + inline void setResolution (glm::vec3 v) { resolution_ = v; } + + // import a source + bool import(Source *source); + + // TODO import session entirely : bool import(); + + glm::ivec2 icon() const override { return glm::ivec2(4, 3); } + +protected: + + void init() override; + + glm::vec3 resolution_; + +}; + class RenderSource : public Source { diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 1b21076..e1a0f0f 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -5,6 +5,7 @@ #include "Decorations.h" #include "Source.h" #include "MediaSource.h" +#include "Session.h" #include "SessionSource.h" #include "PatternSource.h" #include "DeviceSource.h" @@ -413,7 +414,7 @@ void SessionVisitor::visit (MediaSource& s) s.mediaplayer()->accept(*this); } -void SessionVisitor::visit (SessionSource& s) +void SessionVisitor::visit (SessionFileSource& s) { xmlCurrent_->SetAttribute("type", "SessionSource"); @@ -423,6 +424,21 @@ void SessionVisitor::visit (SessionSource& s) path->InsertEndChild( text ); } +void SessionVisitor::visit (SessionGroupSource& s) +{ + xmlCurrent_->SetAttribute("type", "GroupSource"); + + Session *se = s.session(); + + XMLElement *rootgroup = xmlDoc_->NewElement("Session"); + xmlCurrent_->InsertEndChild(rootgroup); + + setRoot(rootgroup); + for (auto iter = se->begin(); iter != se->end(); iter++, setRoot(rootgroup) ) + (*iter)->accept(*this); + +} + void SessionVisitor::visit (RenderSource&) { xmlCurrent_->SetAttribute("type", "RenderSource"); diff --git a/SessionVisitor.h b/SessionVisitor.h index 54814aa..bed9508 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -44,7 +44,8 @@ public: // Sources void visit (Source& s) override; void visit (MediaSource& s) override; - void visit (SessionSource& s) override; + void visit (SessionFileSource& s) override; + void visit (SessionGroupSource& s) override; void visit (RenderSource&) override; void visit (CloneSource& s) override; void visit (PatternSource& s) override; diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index ab8ab11..cab48ee 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -272,6 +272,10 @@ void UserInterface::handleKeyboard() // New Session Mixer::manager().close(); } +// else if (ImGui::IsKeyPressed( GLFW_KEY_SPACE )) { +// // New Session +// Mixer::manager().session()->setActive( !Mixer::manager().session()->active() ); +// } else if (ImGui::IsKeyPressed( GLFW_KEY_L )) { // Logs Settings::application.widget.logs = !Settings::application.widget.logs; @@ -559,7 +563,7 @@ void UserInterface::handleMouse() } // no source is selected else - clear_selection = true; + Mixer::manager().unsetCurrentSource(); } if (clear_selection) { // unset current @@ -620,7 +624,7 @@ void UserInterface::handleMouse() glm::vec2 d = mousepos - mouse_smooth; mouse_smooth += smoothing * d; ImVec2 start = ImVec2(mouse_smooth.x / io.DisplayFramebufferScale.x, mouse_smooth.y / io.DisplayFramebufferScale.y); - ImGui::GetBackgroundDrawList()->AddLine(io.MousePos, start, ImGui::GetColorU32(ImGuiCol_ResizeGripHovered), 5.f); + ImGui::GetBackgroundDrawList()->AddLine(io.MousePos, start, ImGui::GetColorU32(ImGuiCol_HeaderActive), 5.f); } else mouse_smooth = mousepos; @@ -648,9 +652,9 @@ void UserInterface::handleMouse() // Selection area else { ImGui::GetBackgroundDrawList()->AddRect(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, - ImGui::GetColorU32(ImGuiCol_ResizeGripHovered)); + ImGui::GetColorU32(ImGuiCol_HeaderActive)); ImGui::GetBackgroundDrawList()->AddRectFilled(io.MouseClickedPos[ImGuiMouseButton_Left], io.MousePos, - ImGui::GetColorU32(ImGuiCol_ResizeGripHovered, 0.3f)); + ImGui::GetColorU32(ImGuiCol_Header, 0.3f)); // Bounding box multiple sources selection Mixer::manager().view()->select(mouseclic[ImGuiMouseButton_Left], mousepos); diff --git a/View.cpp b/View.cpp index bc152d5..2142633 100644 --- a/View.cpp +++ b/View.cpp @@ -38,6 +38,7 @@ uint View::need_deep_update_ = 1; View::View(Mode m) : mode_(m) { + show_context_menu_ = false; } void View::restoreSettings() @@ -308,7 +309,6 @@ MixingView::MixingView() : View(MIXING), limbo_scale_(1.3f) slider_->color = glm::vec4( COLOR_SLIDER_CIRCLE, 1.0f ); slider_root_->attach(slider_); - stashCircle_ = new Disk(); stashCircle_->scale_ = glm::vec3(0.5f, 0.5f, 1.f); stashCircle_->translation_ = glm::vec3(2.f, -1.0f, 0.f); @@ -412,7 +412,7 @@ std::pair MixingView::pick(glm::vec2 P) // get picking from generic View std::pair pick = View::pick(P); - // deal with internal interactive objects and do not forward + // deal with internal interactive objects if ( pick.first == button_white_ || pick.first == button_black_ ) { RotateToCallback *anim = nullptr; @@ -428,8 +428,6 @@ std::pair MixingView::pick(glm::vec2 P) slider_root_->update_callbacks_.clear(); slider_root_->update_callbacks_.push_back(anim); - // capture this pick - pick = { nullptr, glm::vec2(0.f) }; } else { // get if a source was picked @@ -682,7 +680,7 @@ float RenderView::fading() const return 0.f; } -void RenderView::setResolution(glm::vec3 resolution) +void RenderView::setResolution(glm::vec3 resolution, bool useAlpha) { // use default resolution if invalid resolution is given (default behavior) if (resolution.x < 2.f || resolution.y < 2.f) @@ -698,7 +696,7 @@ void RenderView::setResolution(glm::vec3 resolution) if (!frame_buffer_) // output frame is an RBG Multisamples FrameBuffer - frame_buffer_ = new FrameBuffer(resolution, false, true); + frame_buffer_ = new FrameBuffer(resolution, useAlpha, true); // reset fading setFading(); @@ -810,7 +808,6 @@ GeometryView::GeometryView() : View(GEOMETRY) scene.fg()->attach(overlay_crop_); overlay_crop_->visible_ = false; - show_context_menu_ = false; } void GeometryView::update(float dt) @@ -1035,7 +1032,7 @@ std::pair GeometryView::pick(glm::vec2 P) } // picking on the menu handle: show context menu else if ( pick.first == current->handles_[mode_][Handles::MENU] ) { - show_context_menu_ = true; + openContextMenu(); } // pick on the lock icon; unlock source else if ( pick.first == current->lock_ ) { @@ -1563,9 +1560,43 @@ LayerView::LayerView() : View(LAYER), aspect_ratio(1.f) persp_right_->translation_.z = -0.1f; scene.bg()->attach(persp_right_); - + overlay_group_ = new Group; + overlay_group_->visible_ = false; + overlay_group_icon_ = new Handles(Handles::MENU); + overlay_group_->attach(overlay_group_icon_); + overlay_group_frame_ = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE); + overlay_group_frame_->scale_ = glm::vec3(1.05f, 1.1f, 1.f); + overlay_group_->attach(overlay_group_frame_); + scene.fg()->attach(overlay_group_); } + +void showContextMenuLayer(const char* label) +{ + if (ImGui::BeginPopup(label)) { + + if (ImGui::Selectable( ICON_FA_CUBE " Create group" )){ + + Mixer::manager().groupSelection(); + } + + ImGui::EndPopup(); + } +} + +void LayerView::draw() +{ + View::draw(); + + // display popup menu + if (show_context_menu_) { + ImGui::OpenPopup( "LayerContextMenu" ); + show_context_menu_ = false; + } + showContextMenuLayer("LayerContextMenu"); +} + + void LayerView::update(float dt) { View::update(dt); @@ -1583,6 +1614,49 @@ void LayerView::update(float dt) persp_right_->translation_.x = aspect_ratio + 0.06; } } + + // no overlay by default + overlay_group_->visible_ = false; + + // Test selection for possible group with selection of min 2 sources + if (Mixer::selection().size() > 1) { + + // initialize the verification of the selection + bool candidate_group = true; + BoundingBoxVisitor selection_visitor_bbox; + // start loop on selection + SourceList::iterator it = Mixer::selection().begin(); + float depth_first = (*it)->depth(); + for (; it != Mixer::selection().end(); it++) { + // test if selection is contiguous in layer (i.e. not interrupted) + SourceList::iterator inter = Mixer::manager().session()->find(depth_first, (*it)->depth()); + if ( inter != Mixer::manager().session()->end() && !Mixer::selection().contains(*inter)){ + // NOT a group: there is a source in the session that + // - is between two selected sources (in depth) + // - is not part of the selection + candidate_group = false; + break; + } + + // calculate bounding box of area covered by selection + selection_visitor_bbox.setModelview( scene.ws()->transform_ ); + (*it)->group(mode_)->accept(selection_visitor_bbox); + } + + // the selection is a potential candidate for making a group + if (candidate_group) { + // adjust group overlay to selection + GlmToolkit::AxisAlignedBoundingBox selection_box = selection_visitor_bbox.bbox(); + overlay_group_->scale_ = selection_box.scale(); + overlay_group_->translation_ = selection_box.center(); + // show group overlay + overlay_group_->visible_ = true; + ImVec4 c = ImGuiToolkit::HighlightColor(); + overlay_group_frame_->color = glm::vec4(c.x, c.y, c.z, c.w); + overlay_group_icon_->color = glm::vec4(c.x, c.y, c.z, c.w); + } + } + } bool LayerView::canSelect(Source *s) { @@ -1617,23 +1691,29 @@ std::pair LayerView::pick(glm::vec2 P) // get picking from generic View std::pair pick = View::pick(P); + // deal with internal interactive objects + if ( pick.first == overlay_group_icon_ ) { - // get if a source was picked - Source *s = Mixer::manager().findSource(pick.first); - if (s != nullptr) { - // pick on the lock icon; unlock source - if ( pick.first == s->lock_) { - s->setLocked(false); - pick = { s->locker_, pick.second }; + openContextMenu(); + } + else { + // get if a source was picked + Source *s = Mixer::manager().findSource(pick.first); + if (s != nullptr) { + // pick on the lock icon; unlock source + if ( pick.first == s->lock_) { + s->setLocked(false); + pick = { s->locker_, pick.second }; + } + // pick on the open lock icon; lock source and cancel pick + else if ( pick.first == s->unlock_ ) { + s->setLocked(true); + pick = { nullptr, glm::vec2(0.f) }; + } + // pick a locked source without CTRL key; cancel pick + else if ( s->locked() && !UserInterface::manager().ctrlModifier() ) + pick = { nullptr, glm::vec2(0.f) }; } - // pick on the open lock icon; lock source and cancel pick - else if ( pick.first == s->unlock_ ) { - s->setLocked(true); - pick = { nullptr, glm::vec2(0.f) }; - } - // pick a locked source without CTRL key; cancel pick - else if ( s->locked() && !UserInterface::manager().ctrlModifier() ) - pick = { nullptr, glm::vec2(0.f) }; } return pick; @@ -1664,7 +1744,6 @@ float LayerView::setDepth(Source *s, float d) if ((*node)->translation_.z + DELTA_DEPTH > MAX_DEPTH ) (*node)->translation_.z -= DELTA_DEPTH; } - } // change depth @@ -1942,7 +2021,7 @@ bool TransitionView::canSelect(Source *s) { return ( s!=nullptr && s == transition_source_); } -void TransitionView::attach(SessionSource *ts) +void TransitionView::attach(SessionFileSource *ts) { // store source for later (detatch & interaction) transition_source_ = ts; @@ -2457,7 +2536,7 @@ std::pair AppearanceView::pick(glm::vec2 P) // picking on the menu handle else if ( pick.first == s->handles_[mode_][Handles::MENU] ) { // show context menu - show_context_menu_ = true; + openContextMenu(); } } @@ -2558,7 +2637,7 @@ Source *AppearanceView::getEditOrCurrentSource() void AppearanceView::draw() { // edit view needs to be updated (source changed) - if ( need_edit_update_ ) + if ( need_edit_update_ ) { need_edit_update_ = false; diff --git a/View.h b/View.h index db04f84..d419f00 100644 --- a/View.h +++ b/View.h @@ -9,11 +9,14 @@ class Source; typedef std::list SourceList; -class SessionSource; +class Session; +class SessionFileSource; class Surface; class Symbol; class Mesh; class Frame; +class Disk; +class Handles; class View { @@ -94,6 +97,10 @@ protected: std::string current_action_; uint64_t current_id_; Mode mode_; + + // contex menu + bool show_context_menu_; + inline void openContextMenu() { show_context_menu_ = true; } }; @@ -120,12 +127,11 @@ private: float limbo_scale_; Group *slider_root_; - class Disk *slider_; - class Disk *button_white_; - class Disk *button_black_; - class Disk *stashCircle_; - class Mesh *mixingCircle_; - + Disk *slider_; + Disk *button_white_; + Disk *button_black_; + Disk *stashCircle_; + Mesh *mixingCircle_; }; class RenderView : public View @@ -140,7 +146,7 @@ public: void draw () override; bool canSelect(Source *) override; - void setResolution (glm::vec3 resolution = glm::vec3(0.f)); + void setResolution (glm::vec3 resolution = glm::vec3(0.f), bool useAlpha = false); glm::vec3 resolution() const { return frame_buffer_->resolution(); } void setFading(float f = 0.f); @@ -177,8 +183,6 @@ private: Node *overlay_scaling_cross_; Node *overlay_scaling_grid_; Node *overlay_crop_; - bool show_context_menu_; - }; class LayerView : public View @@ -186,6 +190,7 @@ class LayerView : public View public: LayerView(); + void draw () override; void update (float dt) override; void resize (int) override; int size () override; @@ -202,6 +207,10 @@ private: Mesh *persp_layer_; Mesh *persp_left_, *persp_right_; Group *frame_; + Group *overlay_group_; + Frame *overlay_group_frame_; + Handles *overlay_group_icon_; + }; class TransitionView : public View @@ -219,15 +228,15 @@ public: void arrow (glm::vec2) override; Cursor drag (glm::vec2, glm::vec2) override; - void attach(SessionSource *ts); - class Session *detach(); + void attach(SessionFileSource *ts); + Session *detach(); void play(bool open); private: Surface *output_surface_; Mesh *mark_100ms_, *mark_1s_; Switch *gradient_; - SessionSource *transition_source_; + SessionFileSource *transition_source_; }; @@ -282,7 +291,6 @@ private: Symbol *overlay_rotation_fix_; Node *overlay_rotation_clock_; Symbol *overlay_rotation_clock_hand_; - bool show_context_menu_; // for mask shader draw: 0=cursor, 1=brush, 2=eraser, 3=crop_shape int mask_cursor_paint_; diff --git a/Visitor.h b/Visitor.h index ff2437d..c16e156 100644 --- a/Visitor.h +++ b/Visitor.h @@ -32,7 +32,8 @@ class MediaSource; class PatternSource; class DeviceSource; class GenericStreamSource; -class SessionSource; +class SessionFileSource; +class SessionGroupSource; class RenderSource; class CloneSource; class NetworkSource; @@ -75,7 +76,8 @@ public: virtual void visit (GenericStreamSource&) {} virtual void visit (DeviceSource&) {} virtual void visit (PatternSource&) {} - virtual void visit (SessionSource&) {} + virtual void visit (SessionFileSource&) {} + virtual void visit (SessionGroupSource&) {} virtual void visit (RenderSource&) {} virtual void visit (CloneSource&) {}