diff --git a/src/BoundingBoxVisitor.cpp b/src/BoundingBoxVisitor.cpp index 7eac957..fad5533 100644 --- a/src/BoundingBoxVisitor.cpp +++ b/src/BoundingBoxVisitor.cpp @@ -3,11 +3,9 @@ #include -#include "Log.h" #include "View.h" -#include "Primitives.h" +#include "Scene.h" #include "Source.h" -#include "Decorations.h" #include "BoundingBoxVisitor.h" @@ -57,15 +55,16 @@ void BoundingBoxVisitor::visit(Switch &n) void BoundingBoxVisitor::visit(Primitive &n) { - bbox_.extend(n.bbox().transformed(modelview_)); - -// Log::Info("visitor box (%f, %f)-(%f, %f)", bbox_.min().x, bbox_.min().y, bbox_.max().x, bbox_.max().y); } void BoundingBoxVisitor::visit(Scene &n) { n.ws()->accept(*this); + if (force_){ + n.bg()->accept(*this); + n.fg()->accept(*this); + } } GlmToolkit::AxisAlignedBoundingBox BoundingBoxVisitor::AABB(SourceList l, View *view) diff --git a/src/BoundingBoxVisitor.h b/src/BoundingBoxVisitor.h index d8958bf..4d87fe0 100644 --- a/src/BoundingBoxVisitor.h +++ b/src/BoundingBoxVisitor.h @@ -8,8 +8,8 @@ class View; class BoundingBoxVisitor: public Visitor { - glm::mat4 modelview_; bool force_; + glm::mat4 modelview_; GlmToolkit::AxisAlignedBoundingBox bbox_; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5b1470..e4a98d9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -88,6 +88,7 @@ set(VMIX_SRCS ShmdataBroadcast.cpp SrtReceiverSource.cpp MultiFileRecorder.cpp + DisplaysView.cpp ) ##### diff --git a/src/DisplaysView.cpp b/src/DisplaysView.cpp new file mode 100644 index 0000000..cb1740c --- /dev/null +++ b/src/DisplaysView.cpp @@ -0,0 +1,763 @@ +/* + * This file is part of vimix - video live mixer + * + * **Copyright** (C) 2019-2022 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 +#include +#include + +#include +#include +#include +#include +#include + +#include "ImGuiToolkit.h" + +#include "Mixer.h" +#include "defines.h" +#include "Source.h" +#include "Settings.h" +#include "PickingVisitor.h" +#include "DrawVisitor.h" +#include "Decorations.h" +#include "UserInterfaceManager.h" +#include "BoundingBoxVisitor.h" +#include "RenderingManager.h" + +#include "DisplaysView.h" + + +DisplaysView::DisplaysView() : View(DISPLAYS) +{ + scene.root()->scale_ = glm::vec3(DISPLAYS_DEFAULT_SCALE, DISPLAYS_DEFAULT_SCALE, 1.0f); + // read default settings + if ( Settings::application.views[mode_].name.empty() ) + // no settings found: store application default + saveSettings(); + else + restoreSettings(); + Settings::application.views[mode_].name = "Displays"; + + // Geometry Scene foreground + output_surface_ = new Surface; + output_surface_->visible_ = false; + scene.fg()->attach(output_surface_); +// Frame *border = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE); +// border->color = glm::vec4( COLOR_FRAME, 1.f ); +// scene.fg()->attach(border); + +// // User interface foreground +// // +// // point to show POSITION +// overlay_position_ = new Symbol(Symbol::SQUARE_POINT); +// overlay_position_->scale_ = glm::vec3(0.5f, 0.5f, 1.f); +// scene.fg()->attach(overlay_position_); +// overlay_position_->visible_ = false; +// // cross to show the axis for POSITION +// overlay_position_cross_ = new Symbol(Symbol::GRID); +// overlay_position_cross_->scale_ = glm::vec3(0.5f, 0.5f, 1.f); +// scene.fg()->attach(overlay_position_cross_); +// overlay_position_cross_->visible_ = false; +// // 'clock' : tic marks every 10 degrees for ROTATION +// // with dark background +// overlay_rotation_clock_ = new Group; +// overlay_rotation_clock_tic_ = new Symbol(Symbol::CLOCK); +// overlay_rotation_clock_->attach(overlay_rotation_clock_tic_); +// Symbol *s = new Symbol(Symbol::CIRCLE_POINT); +// s->color = glm::vec4(0.f, 0.f, 0.f, 0.1f); +// s->scale_ = glm::vec3(28.f, 28.f, 1.f); +// s->translation_.z = -0.1; +// overlay_rotation_clock_->attach(s); +// overlay_rotation_clock_->scale_ = glm::vec3(0.25f, 0.25f, 1.f); +// scene.fg()->attach(overlay_rotation_clock_); +// overlay_rotation_clock_->visible_ = false; +// // circle to show fixed-size ROTATION +// overlay_rotation_clock_hand_ = new Symbol(Symbol::CLOCK_H); +// overlay_rotation_clock_hand_->scale_ = glm::vec3(0.25f, 0.25f, 1.f); +// scene.fg()->attach(overlay_rotation_clock_hand_); +// overlay_rotation_clock_hand_->visible_ = false; +// overlay_rotation_fix_ = new Symbol(Symbol::SQUARE); +// overlay_rotation_fix_->scale_ = glm::vec3(0.25f, 0.25f, 1.f); +// scene.fg()->attach(overlay_rotation_fix_); +// overlay_rotation_fix_->visible_ = false; +// // circle to show the center of ROTATION +// overlay_rotation_ = new Symbol(Symbol::CIRCLE); +// overlay_rotation_->scale_ = glm::vec3(0.25f, 0.25f, 1.f); +// scene.fg()->attach(overlay_rotation_); +// overlay_rotation_->visible_ = false; +// // 'grid' : tic marks every 0.1 step for SCALING +// // with dark background +// Group *g = new Group; +// s = new Symbol(Symbol::GRID); +// s->scale_ = glm::vec3(1.655f, 1.655f, 1.f); +// g->attach(s); +// s = new Symbol(Symbol::SQUARE_POINT); +// s->color = glm::vec4(0.f, 0.f, 0.f, 0.1f); +// s->scale_ = glm::vec3(17.f, 17.f, 1.f); +// s->translation_.z = -0.1; +// g->attach(s); +// overlay_scaling_grid_ = g; +// overlay_scaling_grid_->scale_ = glm::vec3(0.3f, 0.3f, 1.f); +// scene.fg()->attach(overlay_scaling_grid_); +// overlay_scaling_grid_->visible_ = false; +// // cross in the square for proportional SCALING +// overlay_scaling_cross_ = new Symbol(Symbol::CROSS); +// overlay_scaling_cross_->scale_ = glm::vec3(0.3f, 0.3f, 1.f); +// scene.fg()->attach(overlay_scaling_cross_); +// overlay_scaling_cross_->visible_ = false; +// // square to show the center of SCALING +// overlay_scaling_ = new Symbol(Symbol::SQUARE); +// overlay_scaling_->scale_ = glm::vec3(0.3f, 0.3f, 1.f); +// scene.fg()->attach(overlay_scaling_); +// overlay_scaling_->visible_ = false; + +// border = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE); +// border->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 0.2f ); +// overlay_crop_ = border; +// scene.fg()->attach(overlay_crop_); +// overlay_crop_->visible_ = false; + + // will be init later + overlay_selection_scale_ = nullptr; + overlay_selection_rotate_ = nullptr; + overlay_selection_stored_status_ = nullptr; + overlay_selection_active_ = false; +} + +void DisplaysView::update(float dt) +{ + 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; +// } +// for (NodeSet::iterator node = scene.fg()->begin(); node != scene.fg()->end(); ++node) { +// (*node)->scale_.x = aspect_ratio; +// } +// output_surface_->setTextureIndex( output->texture() ); + } + + // prevent invalid scaling + float s = CLAMP(scene.root()->scale_.x, DISPLAYS_MIN_SCALE, DISPLAYS_MAX_SCALE); + scene.root()->scale_.x = s; + scene.root()->scale_.y = s; + } + + +} + + +// recenter is called also when RenderingManager detects a change of monitors +void DisplaysView::recenter () +{ + // clear background display of monitors + scene.clearBackground(); + + // fill scene background with the frames to show monitors + std::map _monitors = Rendering::manager().monitors(); + for (auto monitor_iter = _monitors.begin(); + monitor_iter != _monitors.end(); ++monitor_iter) { + + Group *m = new Group; + glm::vec4 rect = DISPLAYS_UNIT * glm::vec4(monitor_iter->second); + m->scale_ = glm::vec3( 0.5f * rect.p, 0.5f * rect.q, 1.f ); + m->translation_ = glm::vec3( rect.x + m->scale_.x, -rect.y - m->scale_.y, 0.f ); + + Surface *surf = new Surface; + m->attach(surf); + + Frame *frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE); + frame->color = glm::vec4( COLOR_MONITOR, 1.f ); + m->attach(frame); + + scene.bg()->attach( m ); + +// g_printerr("- display %f,%f %f,%f\n", rect.x, rect.y, rect.w, rect.z); +// g_printerr(" %f,%f %f,%f\n", m->translation_.x, m->translation_.y, +// m->scale_.x, m->scale_.y); + } + + // calculate screen area required to see the entire scene + BoundingBoxVisitor scene_visitor_bbox(true); + scene.accept(scene_visitor_bbox); + GlmToolkit::AxisAlignedBoundingBox scene_box = scene_visitor_bbox.bbox(); + + // compute scaling to fit the scene box into the scene, modulo a margin ratio + scene.root()->scale_.x = SCENE_UNIT / ( scene_box.scale().x * 1.3f); + scene.root()->scale_.y = scene.root()->scale_.x; + // compute translation to place at the center (considering scaling) + scene.root()->translation_ = -scene.root()->scale_.x * scene_box.center(); + +} + +void DisplaysView::resize ( int scale ) +{ + float z = CLAMP(0.01f * (float) scale, 0.f, 1.f); + z *= z; + z *= DISPLAYS_MAX_SCALE - DISPLAYS_MIN_SCALE; + z += DISPLAYS_MIN_SCALE; + scene.root()->scale_.x = z; + scene.root()->scale_.y = z; +} + +int DisplaysView::size () +{ + float z = (scene.root()->scale_.x - DISPLAYS_MIN_SCALE) / (DISPLAYS_MAX_SCALE - DISPLAYS_MIN_SCALE); + return (int) ( sqrt(z) * 100.f); +} + +void DisplaysView::draw() +{ +// std::vector surfaces; +// std::vector overlays; + +// g_printerr("DisplaysView::draw()\n"); + + + View::draw(); + +// // 0. prepare projection for draw visitors +// glm::mat4 projection = Rendering::manager().Projection(); + +//// // 1. Draw surface of sources in the current workspace +//// DrawVisitor draw_surfaces(surfaces, projection); +//// scene.accept(draw_surfaces); + +//// for( auto it = surfaces.begin(); it!=surfaces.end(); ++it) { +//// delete *it; +//// } + +//// // 2. Draw scene rendering on top (which includes rendering of all visible sources) +//// DrawVisitor draw_rendering(output_surface_, projection, true); +//// scene.accept(draw_rendering); + +// // 3. Draw frames and icons of sources in the current workspace +// DrawVisitor draw_overlays(overlays, projection); +// scene.accept(draw_overlays); + +// // 4. Draw control overlays of current source on top (if selectable) +// if (s!=nullptr && canSelect(s)) { +// s->setMode(Source::CURRENT); +// DrawVisitor dv(s->overlays_[mode_], projection); +// scene.accept(dv); +// } + +// // 5. Finally, draw overlays of view +// DrawVisitor draw_foreground(scene.fg(), projection); +// scene.accept(draw_foreground); + +// display interface + // Locate window at upper left corner +// glm::vec2 P = glm::vec2(-output_surface_->scale_.x - 0.02f, output_surface_->scale_.y + 0.01 ); +// P = Rendering::manager().project(glm::vec3(P, 0.f), scene.root()->transform_, false); +// // Set window position depending on icons size +// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); +// ImGui::SetNextWindowPos(ImVec2(P.x, P.y - 1.5f * ImGui::GetFrameHeight() ), ImGuiCond_Always); +// if (ImGui::Begin("##DisplaysViewOptions", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground +// | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings +// | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus )) +// { +// // style grey +// ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_FRAME_LIGHT, 1.f)); // 1 +// ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f)); +// ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.36f, 0.36f, 0.36f, 0.9f)); +// ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.5f)); +// ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.00f)); +// ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.14f, 0.14f, 0.14f, 0.46f)); +// ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.14f, 0.14f, 0.14f, 0.00f)); +// ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.14f, 0.14f, 0.14f, 0.46f)); // 8 + +// static std::vector< std::pair > icons_ws = { {10,16}, {11,16}, {12,16} }; +// static std::vector< std::string > labels_ws = { "Background", "Workspace", "Foreground" }; +// if ( ImGuiToolkit::ComboIcon (icons_ws, labels_ws, &Settings::application.current_workspace) ){ +// ++View::need_deep_update_; +// } + +// ImGui::PopStyleColor(8); // 14 colors +// ImGui::End(); +// } +// ImGui::PopFont(); + +// // display popup menu +// if (show_context_menu_ == MENU_SOURCE) { +// ImGui::OpenPopup( "DisplaysOutputContextMenu" ); +// show_context_menu_ = MENU_NONE; +// } +// if (ImGui::BeginPopup("DisplaysOutputContextMenu")) { +//// if (s != nullptr) { +//// if (ImGui::Selectable( ICON_FA_EXPAND " Fit" )){ +//// s->group(mode_)->scale_ = glm::vec3(output_surface_->scale_.x/ s->frame()->aspectRatio(), 1.f, 1.f); +//// s->group(mode_)->rotation_.z = 0; +//// s->group(mode_)->translation_ = glm::vec3(0.f); +//// s->touch(); +//// Action::manager().store(s->name() + std::string(": Geometry Fit")); +//// } +//// if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE " Reset" )){ +//// s->group(mode_)->scale_ = glm::vec3(1.f); +//// s->group(mode_)->rotation_.z = 0; +//// s->group(mode_)->crop_ = glm::vec3(1.f); +//// s->group(mode_)->translation_ = glm::vec3(0.f); +//// s->touch(); +//// Action::manager().store(s->name() + std::string(": Geometry Reset")); +//// } +//// if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Reset position" )){ +//// s->group(mode_)->translation_ = glm::vec3(0.f); +//// s->touch(); +//// Action::manager().store(s->name() + std::string(": Reset position")); +//// } +//// if (ImGui::Selectable( ICON_FA_COMPASS " Reset rotation" )){ +//// s->group(mode_)->rotation_.z = 0; +//// s->touch(); +//// Action::manager().store(s->name() + std::string(": Reset rotation")); +//// } +//// if (ImGui::Selectable( ICON_FA_EXPAND_ALT " Reset aspect ratio" )){ +//// s->group(mode_)->scale_.x = s->group(mode_)->scale_.y; +//// s->group(mode_)->scale_.x *= s->group(mode_)->crop_.x / s->group(mode_)->crop_.y; +//// s->touch(); +//// Action::manager().store(s->name() + std::string(": Reset aspect ratio")); +//// } +//// } +// ImGui::EndPopup(); +// } + +} + + +std::pair DisplaysView::pick(glm::vec2 P) +{ + // prepare empty return value + std::pair pick = { nullptr, glm::vec2(0.f) }; + + // unproject mouse coordinate into scene coordinates + glm::vec3 scene_point_ = Rendering::manager().unProject(P); + +// // picking visitor traverses the scene +// PickingVisitor pv(scene_point_); +// scene.accept(pv); + +// // picking visitor found nodes? +// if ( !pv.empty() ) { +// // keep current source active if it is clicked +// Source *current = Mixer::manager().currentSource(); +// if (current != nullptr) { +// if (current->workspace() != Settings::application.current_workspace){ +// current = nullptr; +// } +// else { +// // find if the current source was picked +// auto itp = pv.rbegin(); +// for (; itp != pv.rend(); ++itp){ +// // test if source contains this node +// Source::hasNode is_in_source((*itp).first ); +// if ( is_in_source( current ) ){ +// // a node in the current source was clicked ! +// pick = *itp; +// break; +// } +// } +// // not found: the current source was not clicked +// if (itp == pv.rend()) { +// current = nullptr; +// } +// // picking on the menu handle: show context menu +// else if ( pick.first == current->handles_[mode_][Handles::MENU] ) { +// openContextMenu(MENU_SOURCE); +// } +// // pick on the lock icon; unlock source +// else if ( UserInterface::manager().ctrlModifier() && pick.first == current->lock_ ) { +// lock(current, false); +// pick = { current->locker_, pick.second }; +//// pick = { nullptr, glm::vec2(0.f) }; +// } +// // pick on the open lock icon; lock source and cancel pick +// else if ( UserInterface::manager().ctrlModifier() && pick.first == current->unlock_ ) { +// lock(current, true); +// pick = { nullptr, glm::vec2(0.f) }; +// } +// // pick a locked source ; cancel pick +// else if ( !UserInterface::manager().ctrlModifier() && current->locked() ) { +// pick = { nullptr, glm::vec2(0.f) }; +// } +// } +// } +// // the clicked source changed (not the current source) +// if (current == nullptr) { + +// if (UserInterface::manager().ctrlModifier()) { + +// // default to failed pick +// pick = { nullptr, glm::vec2(0.f) }; + +// // loop over all nodes picked to detect clic on locks +// for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){ +// // get if a source was picked +// Source *s = Mixer::manager().findSource((*itp).first); +// // lock icon of a source (not current) is picked : unlock +// if ( s!=nullptr && (*itp).first == s->lock_) { +// lock(s, false); +// pick = { s->locker_, (*itp).second }; +// break; +// } +// } +// } +// // no lock icon picked, find what else was picked +// if ( pick.first == nullptr) { + +// // loop over all nodes picked +// for (auto itp = pv.rbegin(); itp != pv.rend(); ++itp){ +// // get if a source was picked +// Source *s = Mixer::manager().findSource((*itp).first); +// // accept picked sources in current workspaces +// if ( s!=nullptr && s->workspace() == Settings::application.current_workspace) { +// // a non-locked source is picked (anywhere) +// if ( !s->locked() ) { +// // not in an active selection? don't pick this one! +// if ( !UserInterface::manager().ctrlModifier() && +// Mixer::selection().size() > 1 && +// !Mixer::selection().contains(s)) +// continue; +// // yeah, pick this one (NB: locker_ is just a node in Geometry that is detected) +// pick = { s->locker_, (*itp).second }; +// break; +// } + +// } +// // not a source picked +// else { +// // picked on selection handles +// if ( (*itp).first == overlay_selection_scale_ || (*itp).first == overlay_selection_rotate_ ) { +// pick = (*itp); +// // initiate selection manipulation +// if (overlay_selection_stored_status_) { +// overlay_selection_stored_status_->copyTransform(overlay_selection_); +// overlay_selection_active_ = true; +// } +// break; +// } +// else if ( overlay_selection_icon_ != nullptr && (*itp).first == overlay_selection_icon_ ) { +// pick = (*itp); +// openContextMenu(MENU_SELECTION); +// break; +// } +// } +// } +// } +// } +// } + + return pick; +} + +bool DisplaysView::canSelect(Source *s) { + + return ( s!=nullptr && View::canSelect(s) && s->ready() && s->active() && s->workspace() == Settings::application.current_workspace); +} + + +void DisplaysView::applySelectionTransform(glm::mat4 M) +{ +// for (auto sit = Mixer::selection().begin(); sit != Mixer::selection().end(); ++sit){ +// // recompute all from matrix transform +// glm::mat4 transform = M * (*sit)->stored_status_->transform_; +// glm::vec3 tra, rot, sca; +// GlmToolkit::inverse_transform(transform, tra, rot, sca); +// (*sit)->group(mode_)->translation_ = tra; +// (*sit)->group(mode_)->scale_ = sca; +// (*sit)->group(mode_)->rotation_ = rot; +// // will have to be updated +// (*sit)->touch(); +// } +} + +View::Cursor DisplaysView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair pick) +{ + View::Cursor ret = Cursor(); + + // grab coordinates in scene-View reference frame + glm::vec3 scene_from = Rendering::manager().unProject(from, scene.root()->transform_); + glm::vec3 scene_to = Rendering::manager().unProject(to, scene.root()->transform_); + glm::vec3 scene_translation = scene_to - scene_from; + + // No source is given + if (!s) { + +// // possibly grabing the selection overlay handles +// if (overlay_selection_ && overlay_selection_active_ ) { + +// // rotation center to selection position +// glm::mat4 T = glm::translate(glm::identity(), overlay_selection_stored_status_->translation_); +// glm::vec4 selection_from = glm::inverse(T) * glm::vec4( scene_from, 1.f ); +// glm::vec4 selection_to = glm::inverse(T) * glm::vec4( scene_to, 1.f ); + +// // calculate scaling of selection +// float factor = glm::length( glm::vec2( selection_to ) ) / glm::length( glm::vec2( selection_from ) ); +// glm::mat4 S = glm::scale(glm::identity(), glm::vec3(factor, factor, 1.f)); + +// // if interaction with selection SCALING handle +// if (pick.first == overlay_selection_scale_) { + +// // show manipulation overlay +// overlay_scaling_cross_->visible_ = true; +// overlay_scaling_grid_->visible_ = false; +// overlay_scaling_->visible_ = true; +// overlay_scaling_->translation_.x = overlay_selection_stored_status_->translation_.x; +// overlay_scaling_->translation_.y = overlay_selection_stored_status_->translation_.y; +// overlay_scaling_->rotation_.z = overlay_selection_stored_status_->rotation_.z; +// overlay_scaling_->update(0); +// overlay_scaling_cross_->copyTransform(overlay_scaling_); +// overlay_scaling_->color = overlay_selection_icon_->color; +// overlay_scaling_cross_->color = overlay_selection_icon_->color; + +// // apply to selection overlay +// glm::vec4 vec = S * glm::vec4( overlay_selection_stored_status_->scale_, 0.f ); +// overlay_selection_->scale_ = glm::vec3(vec); + +// // apply to selection sources +// // NB: complete transform matrix (right to left) : move to center, scale and move back +// glm::mat4 M = T * S * glm::inverse(T); +// applySelectionTransform(M); + +// // store action in history +// current_action_ = "Scale selection"; +// ret.type = Cursor_ResizeNWSE; +// } +// // if interaction with selection ROTATION handle +// else if (pick.first == overlay_selection_rotate_) { + +// // show manipulation overlay +// overlay_rotation_->visible_ = true; +// overlay_rotation_->translation_.x = overlay_selection_stored_status_->translation_.x; +// overlay_rotation_->translation_.y = overlay_selection_stored_status_->translation_.y; +// overlay_rotation_->update(0); +// overlay_rotation_->color = overlay_selection_icon_->color; +// overlay_rotation_fix_->visible_ = false; +// overlay_rotation_fix_->copyTransform(overlay_rotation_); +// overlay_rotation_fix_->color = overlay_selection_icon_->color; + +// // cancel out scaling with SHIFT modifier key +// if (UserInterface::manager().shiftModifier()) { +// overlay_rotation_fix_->visible_ = true; +// float scale_factor = glm::length( glm::vec2( overlay_selection_->scale_ ) ) / glm::length( glm::vec2( overlay_selection_stored_status_->scale_ ) ); +// S = glm::scale(glm::identity(), glm::vec3(scale_factor, scale_factor, 1.f)); +// } + +// // compute rotation angle +// float angle = glm::orientedAngle( glm::normalize(glm::vec2(selection_from)), glm::normalize(glm::vec2(selection_to))); + +// // apply to selection overlay +// glm::vec4 vec = S * glm::vec4( overlay_selection_stored_status_->scale_, 0.f ); +// overlay_selection_->scale_ = glm::vec3(vec); +// overlay_selection_->rotation_.z = overlay_selection_stored_status_->rotation_.z + angle; + +// // POST-CORRECTION ; discretized rotation with ALT +// if (UserInterface::manager().altModifier()) { +// int degrees = int( glm::degrees(overlay_selection_->rotation_.z) ); +// degrees = (degrees / 10) * 10; +// overlay_selection_->rotation_.z = glm::radians( float(degrees) ); +// angle = overlay_selection_->rotation_.z - overlay_selection_stored_status_->rotation_.z; +// overlay_rotation_clock_->visible_ = true; +// overlay_rotation_clock_->copyTransform(overlay_rotation_); +// overlay_rotation_clock_tic_->color = overlay_selection_icon_->color; +// } + +// // apply to selection sources +// // NB: complete transform matrix (right to left) : move to center, rotate, scale and move back +// glm::mat4 R = glm::rotate(glm::identity(), angle, glm::vec3(0.f, 0.f, 1.f) ); +// glm::mat4 M = T * S * R * glm::inverse(T); +// applySelectionTransform(M); + +// // store action in history +// current_action_ = "Scale and rotate selection"; +// ret.type = Cursor_Hand; +// } + +// } + + // update cursor + return ret; + } + +// Group *sourceNode = s->group(mode_); // groups_[View::GEOMETRY] + +// // make sure matrix transform of stored status is updated +// s->stored_status_->update(0); +// // grab coordinates in source-root reference frame +// glm::vec4 source_from = glm::inverse(s->stored_status_->transform_) * glm::vec4( scene_from, 1.f ); +// glm::vec4 source_to = glm::inverse(s->stored_status_->transform_) * glm::vec4( scene_to, 1.f ); +// glm::vec3 source_scaling = glm::vec3(source_to) / glm::vec3(source_from); + + // which manipulation to perform? + std::ostringstream info; + + // store action in history + current_action_ = s->name() + ": " + info.str(); + + // update cursor + ret.info = info.str(); + return ret; +} + +void DisplaysView::terminate(bool force) +{ + View::terminate(force); + +// // hide all view overlays +// overlay_position_->visible_ = false; +// overlay_position_cross_->visible_ = false; +// overlay_rotation_clock_->visible_ = false; +// overlay_rotation_clock_hand_->visible_ = false; +// overlay_rotation_fix_->visible_ = false; +// overlay_rotation_->visible_ = false; +// overlay_scaling_grid_->visible_ = false; +// overlay_scaling_cross_->visible_ = false; +// overlay_scaling_->visible_ = false; +// overlay_crop_->visible_ = false; + +// // restore possible color change after selection operation +// overlay_rotation_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); +// overlay_rotation_fix_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); +// overlay_rotation_clock_tic_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); +// overlay_scaling_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); +// overlay_scaling_cross_->color = glm::vec4(1.f, 1.f, 1.f, 0.8f); + + // restore of all handles overlays +// glm::vec2 c(0.f, 0.f); +// for (auto sit = Mixer::manager().session()->begin(); +// sit != Mixer::manager().session()->end(); ++sit){ + +// (*sit)->handles_[mode_][Handles::RESIZE]->overlayActiveCorner(c); +// (*sit)->handles_[mode_][Handles::RESIZE_H]->overlayActiveCorner(c); +// (*sit)->handles_[mode_][Handles::RESIZE_V]->overlayActiveCorner(c); +// (*sit)->handles_[mode_][Handles::RESIZE]->visible_ = true; +// (*sit)->handles_[mode_][Handles::RESIZE_H]->visible_ = true; +// (*sit)->handles_[mode_][Handles::RESIZE_V]->visible_ = true; +// (*sit)->handles_[mode_][Handles::SCALE]->visible_ = true; +// (*sit)->handles_[mode_][Handles::ROTATE]->visible_ = true; +// (*sit)->handles_[mode_][Handles::CROP]->visible_ = true; +// (*sit)->handles_[mode_][Handles::MENU]->visible_ = true; +// } + + overlay_selection_active_ = false; +} + +void DisplaysView::arrow (glm::vec2 movement) +{ + static float accumulator = 0.f; + accumulator += dt_; + + 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; + +// bool first = true; +// glm::vec3 delta_translation(0.f); +// for (auto it = Mixer::selection().begin(); it != Mixer::selection().end(); ++it) { + +// // individual move with SHIFT +// if ( !Source::isCurrent(*it) && UserInterface::manager().shiftModifier() ) +// continue; + +// Group *sourceNode = (*it)->group(mode_); +// glm::vec3 dest_translation(0.f); + +// if (first) { +// // dest starts at current +// dest_translation = sourceNode->translation_; + +// // + ALT : discrete displacement +// if (UserInterface::manager().altModifier()) { +// if (accumulator > 100.f) { +// dest_translation += glm::sign(gl_delta) * 0.11f; +// dest_translation.x = ROUND(dest_translation.x, 10.f); +// dest_translation.y = ROUND(dest_translation.y, 10.f); +// accumulator = 0.f; +// } +// else +// break; +// } +// else { +// // normal case: dest += delta +// dest_translation += gl_delta * ARROWS_MOVEMENT_FACTOR * dt_; +// accumulator = 0.f; +// } + +// // store action in history +// std::ostringstream info; +// info << "Position " << std::fixed << std::setprecision(3) << sourceNode->translation_.x; +// info << ", " << sourceNode->translation_.y ; +// current_action_ = (*it)->name() + ": " + info.str(); + +// // delta for others to follow +// delta_translation = dest_translation - sourceNode->translation_; +// } +// else { +// // dest = current + delta from first +// dest_translation = sourceNode->translation_ + delta_translation; +// } + +// // apply & request update +// sourceNode->translation_ = dest_translation; +// (*it)->touch(); + +// first = false; +// } +} + +void DisplaysView::updateSelectionOverlay() +{ + View::updateSelectionOverlay(); + +// // create first +// if (overlay_selection_scale_ == nullptr) { + +// overlay_selection_stored_status_ = new Group; +// overlay_selection_scale_ = new Handles(Handles::SCALE); +// overlay_selection_->attach(overlay_selection_scale_); +// overlay_selection_rotate_ = new Handles(Handles::ROTATE); +// overlay_selection_->attach(overlay_selection_rotate_); +// } + +// if (overlay_selection_->visible_) { + +// if ( !overlay_selection_active_) { + +// // calculate ORIENTED bbox on selection +// GlmToolkit::OrientedBoundingBox selection_box = BoundingBoxVisitor::OBB(Mixer::selection().getCopy(), this); + +// // apply transform +// overlay_selection_->rotation_ = selection_box.orientation; +// overlay_selection_->scale_ = selection_box.aabb.scale(); +// glm::mat4 rot = glm::rotate(glm::identity(), selection_box.orientation.z, glm::vec3(0.f, 0.f, 1.f) ); +// glm::vec4 vec = rot * glm::vec4(selection_box.aabb.center(), 1.f); +// overlay_selection_->translation_ = glm::vec3(vec); +// } + +// // cosmetics +// overlay_selection_scale_->color = overlay_selection_icon_->color; +// overlay_selection_rotate_->color = overlay_selection_icon_->color; +// } +} + diff --git a/src/DisplaysView.h b/src/DisplaysView.h new file mode 100644 index 0000000..e05ea42 --- /dev/null +++ b/src/DisplaysView.h @@ -0,0 +1,50 @@ +#ifndef DISPLAYSVIEW_H +#define DISPLAYSVIEW_H + +#include "View.h" + +class DisplaysView : public View +{ +public: + DisplaysView(); + // non assignable class + DisplaysView(DisplaysView const&) = delete; + DisplaysView& operator=(DisplaysView const&) = delete; + + void draw () override; + void update (float dt) override; + void resize (int) override; + int size () override; + bool canSelect(Source *) override; + void recenter () override; + + std::pair pick(glm::vec2 P) override; + Cursor grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair pick) override; + void terminate(bool force) override; + void arrow (glm::vec2) override; + +private: + Surface *output_surface_; + Node *overlay_position_; + Node *overlay_position_cross_; + Symbol *overlay_rotation_; + Symbol *overlay_rotation_fix_; + Group *overlay_rotation_clock_; + Symbol *overlay_rotation_clock_tic_; + Node *overlay_rotation_clock_hand_; + Symbol *overlay_scaling_; + Symbol *overlay_scaling_cross_; + Node *overlay_scaling_grid_; + Node *overlay_crop_; + + void updateSelectionOverlay() override; + bool overlay_selection_active_; + Group *overlay_selection_stored_status_; + Handles *overlay_selection_scale_; + Handles *overlay_selection_rotate_; + + void applySelectionTransform(glm::mat4 M); +}; + + +#endif // DISPLAYSVIEW_H diff --git a/src/GeometryView.cpp b/src/GeometryView.cpp index d982102..2f71385 100644 --- a/src/GeometryView.cpp +++ b/src/GeometryView.cpp @@ -39,7 +39,6 @@ #include "UserInterfaceManager.h" #include "BoundingBoxVisitor.h" #include "ActionManager.h" -#include "Log.h" #include "GeometryView.h" diff --git a/src/Mixer.cpp b/src/Mixer.cpp index d3de9d0..5533dde 100644 --- a/src/Mixer.cpp +++ b/src/Mixer.cpp @@ -30,13 +30,11 @@ #include #include -#include "tinyxml2Toolkit.h" #include "defines.h" #include "Settings.h" #include "Log.h" #include "View.h" -#include "ImageShader.h" #include "BaseToolkit.h" #include "SystemToolkit.h" #include "SessionCreator.h" @@ -56,7 +54,7 @@ #include "InfoVisitor.h" #include "ActionManager.h" #include "MixingGroup.h" -#include "Streamer.h" +#include "FrameGrabber.h" #include "Mixer.h" @@ -230,6 +228,7 @@ void Mixer::update() layer_.update(dt_); appearance_.update(dt_); transition_.update(dt_); + displays_.update(dt_); // deep update was performed if (View::need_deep_update_ > 0) @@ -493,7 +492,7 @@ bool Mixer::replaceSource(Source *from, Source *to) to->group(View::MIXING)->copyTransform( from->group(View::MIXING) ); to->group(View::GEOMETRY)->copyTransform( from->group(View::GEOMETRY) ); to->group(View::LAYER)->copyTransform( from->group(View::LAYER) ); - to->group(View::MIXING)->copyTransform( from->group(View::MIXING) ); + to->group(View::TEXTURE)->copyTransform( from->group(View::TEXTURE) ); // TODO copy all filters @@ -1107,6 +1106,9 @@ void Mixer::setView(View::Mode m) } switch (m) { + case View::DISPLAYS: + current_view_ = &displays_; + break; case View::TRANSITION: current_view_ = &transition_; break; @@ -1145,6 +1147,8 @@ void Mixer::setView(View::Mode m) View *Mixer::view(View::Mode m) { switch (m) { + case View::DISPLAYS: + return &displays_; case View::TRANSITION: return &transition_; case View::GEOMETRY: diff --git a/src/Mixer.h b/src/Mixer.h index 92cf39a..867316d 100644 --- a/src/Mixer.h +++ b/src/Mixer.h @@ -6,6 +6,7 @@ #include "LayerView.h" #include "TextureView.h" #include "TransitionView.h" +#include "DisplaysView.h" #include "Session.h" #include "Selection.h" @@ -153,6 +154,7 @@ protected: LayerView layer_; TextureView appearance_; TransitionView transition_; + DisplaysView displays_; bool busy_; float dt_; diff --git a/src/Settings.cpp b/src/Settings.cpp index c8ca9f7..40216cb 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -202,8 +202,8 @@ void Settings::Save(uint64_t runtime) // bloc views { XMLElement *viewsNode = xmlDoc.NewElement( "Views" ); - // save current view only if [mixing, geometry, layers, appearance] - int v = application.current_view > 4 ? 1 : application.current_view; + // do not save Transition view as current + int v = application.current_view == 5 ? 1 : application.current_view; viewsNode->SetAttribute("current", v); viewsNode->SetAttribute("workspace", application.current_workspace); diff --git a/src/Source.cpp b/src/Source.cpp index 102add4..ee8f354 100644 --- a/src/Source.cpp +++ b/src/Source.cpp @@ -24,7 +24,6 @@ #include #include "defines.h" -#include "Log.h" #include "FrameBuffer.h" #include "Decorations.h" #include "Resource.h" @@ -32,10 +31,7 @@ #include "ImageShader.h" #include "ImageProcessingShader.h" #include "BaseToolkit.h" -#include "SystemToolkit.h" #include "MixingGroup.h" -#include "Metronome.h" -#include "ControlManager.h" #include "SourceCallback.h" #include "CloneSource.h" @@ -553,7 +549,7 @@ void Source::attach(FrameBuffer *renderbuffer) initial_1_->translation_.x = 0.4f - renderbuffer_->aspectRatio(); // scale all icon nodes to match aspect ratio - for (int v = View::MIXING; v < View::INVALID; v++) { + for (int v = View::MIXING; v <= View::TRANSITION; v++) { NodeSet::iterator node; for (node = groups_[(View::Mode) v]->begin(); node != groups_[(View::Mode) v]->end(); ++node) { diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index 2fa534a..9a6a64e 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -387,6 +387,8 @@ void UserInterface::handleKeyboard() Mixer::manager().setView(View::LAYER); else if (ImGui::IsKeyPressed( GLFW_KEY_F4, false )) Mixer::manager().setView(View::TEXTURE); + else if (ImGui::IsKeyPressed( GLFW_KEY_F5, false )) + Mixer::manager().setView(View::DISPLAYS); else if (ImGui::IsKeyPressed( GLFW_KEY_F9, false )) StartScreenshot(); else if (ImGui::IsKeyPressed( GLFW_KEY_F10, false )) @@ -6201,7 +6203,7 @@ void Navigator::Render() height_ = ImGui::GetIO().DisplaySize.y; // cover vertically const float icon_width = width_ - 2.f * style.WindowPadding.x; // icons keep padding const ImVec2 iconsize(icon_width, icon_width); - const float sourcelist_height = height_ - 4.5f * icon_width - 5.f * style.WindowPadding.y; // space for 4 icons of view + const float sourcelist_height = height_ - 5.5f * icon_width - 5.f * style.WindowPadding.y; // space for 4 icons of view // hack to show more sources if not enough space; make source icons smaller... ImVec2 sourceiconsize(icon_width, icon_width); @@ -6216,7 +6218,7 @@ void Navigator::Render() { ImDrawList* draw_list = ImGui::GetWindowDrawList(); - if (Settings::application.current_view < View::TRANSITION) { + if (Settings::application.current_view != View::TRANSITION) { // the "=" icon for menu if (ImGui::Selectable( ICON_FA_BARS, &selected_button[NAV_MENU], 0, iconsize)) @@ -6303,34 +6305,41 @@ void Navigator::Render() bool selected_view[View::INVALID] = { }; selected_view[ Settings::application.current_view ] = true; int previous_view = Settings::application.current_view; - if (ImGui::Selectable( ICON_FA_BULLSEYE, &selected_view[1], 0, iconsize)) + if (ImGui::Selectable( ICON_FA_BULLSEYE, &selected_view[View::MIXING], 0, iconsize)) { Mixer::manager().setView(View::MIXING); view_pannel_visible = previous_view == Settings::application.current_view; } if (ImGui::IsItemHovered()) tooltip = {"Mixing ", "F1"}; - if (ImGui::Selectable( ICON_FA_OBJECT_UNGROUP , &selected_view[2], 0, iconsize)) + if (ImGui::Selectable( ICON_FA_OBJECT_UNGROUP , &selected_view[View::GEOMETRY], 0, iconsize)) { Mixer::manager().setView(View::GEOMETRY); view_pannel_visible = previous_view == Settings::application.current_view; } if (ImGui::IsItemHovered()) tooltip = {"Geometry ", "F2"}; - if (ImGui::Selectable( ICON_FA_LAYER_GROUP, &selected_view[3], 0, iconsize)) + if (ImGui::Selectable( ICON_FA_LAYER_GROUP, &selected_view[View::LAYER], 0, iconsize)) { Mixer::manager().setView(View::LAYER); view_pannel_visible = previous_view == Settings::application.current_view; } if (ImGui::IsItemHovered()) tooltip = {"Layers ", "F3"}; - if (ImGui::Selectable( ICON_FA_CHESS_BOARD, &selected_view[4], 0, iconsize)) + if (ImGui::Selectable( ICON_FA_CHESS_BOARD, &selected_view[View::TEXTURE], 0, iconsize)) { Mixer::manager().setView(View::TEXTURE); view_pannel_visible = previous_view == Settings::application.current_view; } if (ImGui::IsItemHovered()) tooltip = {"Texturing ", "F4"}; + if (ImGui::Selectable( ICON_FA_TV, &selected_view[View::DISPLAYS], 0, iconsize)) + { + Mixer::manager().setView(View::DISPLAYS); + view_pannel_visible = previous_view == Settings::application.current_view; + } + if (ImGui::IsItemHovered()) + tooltip = {"Displays ", "F5"}; ImVec2 pos = ImGui::GetCursorPos(); ImGui::SetCursorPos(pos + ImVec2(0.f, style.WindowPadding.y)); @@ -7811,7 +7820,7 @@ void Navigator::RenderMainPannelVimix() ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); ImGui::SameLine(0, 0.5f * ImGui::GetTextLineHeight()); - if ( ImGuiToolkit::IconButton( ICON_FA_WINDOW_MAXIMIZE ) ) + if ( ImGuiToolkit::IconButton( ICON_FA_CROW ) ) UserInterface::manager().outputcontrol.setVisible(!Settings::application.widget.preview); if (ImGui::IsItemHovered()) tooltip_ = { TOOLTIP_OUTPUT, SHORTCUT_OUTPUT}; diff --git a/src/UserInterfaceManager.h b/src/UserInterfaceManager.h index de26e9b..305bdca 100644 --- a/src/UserInterfaceManager.h +++ b/src/UserInterfaceManager.h @@ -28,7 +28,7 @@ #define IMGUI_TITLE_HELP ICON_FA_LIFE_RING " Help" #define IMGUI_TITLE_TOOLBOX ICON_FA_HAMSA " Guru Toolbox" #define IMGUI_TITLE_SHADEREDITOR ICON_FA_CODE " Shader Editor" -#define IMGUI_TITLE_PREVIEW ICON_FA_WINDOW_MAXIMIZE " Output" +#define IMGUI_TITLE_PREVIEW ICON_FA_CROW " Output" #define MENU_NEW_FILE ICON_FA_FILE " New" #define SHORTCUT_NEW_FILE CTRL_MOD "W" diff --git a/src/View.cpp b/src/View.cpp index b434eae..1d6c647 100644 --- a/src/View.cpp +++ b/src/View.cpp @@ -34,10 +34,8 @@ #include "PickingVisitor.h" #include "Decorations.h" #include "Mixer.h" -#include "UserInterfaceManager.h" #include "BoundingBoxVisitor.h" #include "ActionManager.h" -#include "Log.h" #include "View.h" @@ -201,14 +199,14 @@ void View::recenter() // if the updated (translated) view does not contains the entire scene // we shall scale the view to fit the scene - if ( !updated_view_box.contains(scene_box)) { + if (!updated_view_box.contains(scene_box)) { glm::vec3 view_extend = updated_view_box.max() - updated_view_box.min(); updated_view_box.extend(scene_box); glm::vec3 scene_extend = scene_box.max() - scene_box.min(); glm::vec3 scale = view_extend / scene_extend ; float z = scene.root()->scale_.x; - z = CLAMP( z * MIN(scale.x, scale.y), MIXING_MIN_SCALE, MIXING_MAX_SCALE); + z = CLAMP( z * MIN(scale.x, scale.y), MIN_SCALE, MAX_SCALE); scene.root()->scale_.x = z; scene.root()->scale_.y = z; diff --git a/src/View.h b/src/View.h index 9e96b6c..9b18141 100644 --- a/src/View.h +++ b/src/View.h @@ -29,7 +29,8 @@ public: LAYER = 3, TEXTURE = 4, TRANSITION = 5, - INVALID = 6 + DISPLAYS = 6, + INVALID = 7 } Mode; inline Mode mode () const { return mode_; } diff --git a/src/defines.h b/src/defines.h index b80f505..8e8c1c4 100644 --- a/src/defines.h +++ b/src/defines.h @@ -67,6 +67,10 @@ #define TRANSITION_DEFAULT_SCALE 5.0f #define TRANSITION_MIN_DURATION 0.2f #define TRANSITION_MAX_DURATION 10.f +#define DISPLAYS_UNIT 0.001f +#define DISPLAYS_DEFAULT_SCALE 1.4f +#define DISPLAYS_MIN_SCALE 0.4f +#define DISPLAYS_MAX_SCALE 7.0f #define ARROWS_MOVEMENT_FACTOR 0.1f #define SESSION_THUMBNAIL_HEIGHT 120.f #define RECORD_MAX_TIMEOUT 1200000 @@ -104,6 +108,7 @@ #define COLOR_LIMBO_CIRCLE 0.173f, 0.173f, 0.173f #define COLOR_SLIDER_CIRCLE 0.11f, 0.11f, 0.11f #define COLOR_STASH_CIRCLE 0.06f, 0.06f, 0.06f +#define COLOR_MONITOR 0.2f, 0.85f, 0.85f #define OSC_PORT_RECV_DEFAULT 7000 #define OSC_PORT_SEND_DEFAULT 7001