Implementation of Custom Output area in Window Displays View

Changed the 'Scaled' mode of window draw to allow custom centering and scaling of the output framebuffer in the window. Use DisplaysView to grab handles of the output frame. Save all windows custom output scaling in Settings.
This commit is contained in:
Bruno Herbelin
2023-03-12 00:31:41 +01:00
parent 7870e3cfce
commit 811b270bae
8 changed files with 328 additions and 151 deletions

Binary file not shown.

View File

@@ -21,11 +21,14 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include <glad/glad.h> #include <glad/glad.h>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/matrix_access.hpp> #include <glm/gtc/matrix_access.hpp>
#include <glm/gtx/vector_angle.hpp> #include <glm/gtx/vector_angle.hpp>
//#include <iostream>
//#include <glm/gtx/io.hpp>
#include "ImGuiToolkit.h" #include "ImGuiToolkit.h"
#include "imgui_internal.h" #include "imgui_internal.h"
@@ -64,18 +67,25 @@ DisplaysView::DisplaysView() : View(DISPLAYS)
// create and attach all window manipulation objects // create and attach all window manipulation objects
windows_ = std::vector<WindowPreview>(MAX_OUTPUT_WINDOW); windows_ = std::vector<WindowPreview>(MAX_OUTPUT_WINDOW);
for (auto w = windows_.begin(); w != windows_.end(); ++w){ for (auto w = windows_.begin(); w != windows_.end(); ++w){
// root node // root node
w->root_ = new Group; w->root_ = new Group;
scene.ws()->attach(w->root_); scene.ws()->attach(w->root_);
w->root_->visible_ = false; w->root_->visible_ = false;
// title bar
w->title_ = new Surface (new Shader);
w->title_->shader()->color = glm::vec4( COLOR_WINDOW, 1.f );
w->title_->scale_ = glm::vec3(1.002f, WINDOW_TITLEBAR_HEIGHT, 1.f);
w->title_->translation_ = glm::vec3(0.f, 1.f + WINDOW_TITLEBAR_HEIGHT, 0.f);
w->root_->attach(w->title_);
// surface background and texture // surface background and texture
w->surface_ = new Surface; w->renderbuffer_ = new FrameBuffer(1024, 1024); // TODO delete on close
w->root_->attach(w->surface_);
w->shader_ = new ImageFilteringShader; w->shader_ = new ImageFilteringShader;
w->shader_->setCode( _whitebalance.code().first ); w->shader_->setCode( _whitebalance.code().first );
w->render_ = new Surface(w->shader_); w->surface_ = new FrameBufferSurface(w->renderbuffer_, w->shader_);
w->root_->attach(w->render_); w->root_->attach(w->surface_);
w->output_render_ = new Surface;
// icon if disabled // icon if disabled
w->icon_ = new Handles(Handles::EYESLASHED); w->icon_ = new Handles(Handles::EYESLASHED);
w->icon_->visible_ = false; w->icon_->visible_ = false;
@@ -91,6 +101,15 @@ DisplaysView::DisplaysView() : View(DISPLAYS)
// overlays_ [1] is for active frame // overlays_ [1] is for active frame
Group *g = new Group; Group *g = new Group;
w->overlays_->attach(g); w->overlays_->attach(g);
// Output frame
w->output_frame_ = new Group;
w->output_frame_->visible_ = false;
frame = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
frame->color = glm::vec4( COLOR_FRAME, 1.f );
w->output_frame_->attach(frame);
w->output_handles_ = new Handles(Handles::RESIZE);
w->output_handles_->color = glm::vec4( COLOR_FRAME, 1.f );
w->output_frame_->attach(w->output_handles_);
// Overlay menu icon // Overlay menu icon
w->menu_ = new Handles(Handles::MENU); w->menu_ = new Handles(Handles::MENU);
w->menu_->color = glm::vec4( COLOR_WINDOW, 1.f ); w->menu_->color = glm::vec4( COLOR_WINDOW, 1.f );
@@ -99,6 +118,7 @@ DisplaysView::DisplaysView() : View(DISPLAYS)
frame = new Frame(Frame::SHARP, Frame::LARGE, Frame::NONE); frame = new Frame(Frame::SHARP, Frame::LARGE, Frame::NONE);
frame->color = glm::vec4( COLOR_WINDOW, 1.f ); frame->color = glm::vec4( COLOR_WINDOW, 1.f );
g->attach(frame); g->attach(frame);
g->attach(w->output_frame_);
// Overlay has two modes : window or fullscreen // Overlay has two modes : window or fullscreen
w->mode_ = new Switch; w->mode_ = new Switch;
g->attach(w->mode_); g->attach(w->mode_);
@@ -111,12 +131,12 @@ DisplaysView::DisplaysView() : View(DISPLAYS)
w->fullscreen_->scale_ = glm::vec3(2.f, 2.f, 1.f); w->fullscreen_->scale_ = glm::vec3(2.f, 2.f, 1.f);
w->fullscreen_->color = glm::vec4( COLOR_WINDOW, 1.f ); w->fullscreen_->color = glm::vec4( COLOR_WINDOW, 1.f );
w->mode_->attach(w->fullscreen_); w->mode_->attach(w->fullscreen_);
// title bar // // title bar
w->title_ = new Surface (new Shader); // w->title_ = new Surface (new Shader);
w->title_->shader()->color = glm::vec4( COLOR_WINDOW, 1.f ); // w->title_->shader()->color = glm::vec4( COLOR_WINDOW, 1.f );
w->title_->scale_ = glm::vec3(1.002f, WINDOW_TITLEBAR_HEIGHT, 1.f); // w->title_->scale_ = glm::vec3(1.002f, WINDOW_TITLEBAR_HEIGHT, 1.f);
w->title_->translation_ = glm::vec3(0.f, 1.f + WINDOW_TITLEBAR_HEIGHT, 0.f); // w->title_->translation_ = glm::vec3(0.f, 1.f + WINDOW_TITLEBAR_HEIGHT, 0.f);
w->root_->attach(w->title_); // w->root_->attach(w->title_);
// default to not active & window overlay frame // default to not active & window overlay frame
w->overlays_->setActive(0); w->overlays_->setActive(0);
w->mode_->setActive(0); w->mode_->setActive(0);
@@ -125,11 +145,9 @@ DisplaysView::DisplaysView() : View(DISPLAYS)
// initial behavior: no window selected, no menu // initial behavior: no window selected, no menu
show_window_menu_ = false; show_window_menu_ = false;
current_window_ = -1; current_window_ = -1;
current_window_status_ = new Group; current_window_status_ = new Group;
current_output_status_ = new Group;
draw_pending_ = false; draw_pending_ = false;
// display actions : 0 = move output, 1 paint, 2 erase
display_action_ = 0;
output_ar = 1.f; output_ar = 1.f;
} }
@@ -141,8 +159,34 @@ void DisplaysView::update(float dt)
if ( Mixer::manager().view() == this ) { if ( Mixer::manager().view() == this ) {
// update rendering of render frame // update rendering of render frame
for (int i = 0; i < MAX_OUTPUT_WINDOW; ++i) for (int i = 0; i < MAX_OUTPUT_WINDOW; ++i) {
windows_[i].render_->setTextureIndex( Rendering::manager().outputWindow(i).texture() ); windows_[i].output_render_->setTextureIndex( Rendering::manager().outputWindow(i).texture() );
// update visible flag
windows_[i].root_->visible_ = i < Settings::application.num_output_windows;
windows_[i].icon_->visible_ = Settings::application.render.disabled;
// Rendering of output is scaled to content and manipulated by output frame
if (Settings::application.windows[i+1].scaled) {
windows_[i].output_render_->scale_ = windows_[i].output_frame_->scale_;
windows_[i].output_render_->translation_ = windows_[i].output_frame_->translation_;
// show output frame
windows_[i].output_frame_->visible_ = true;
}
// Rendering of output is adjusted to match aspect ratio of framebuffer
else {
float out_ar = windows_[i].root_->scale_.x / windows_[i].root_->scale_.y;
if (output_ar < out_ar)
windows_[i].output_render_->scale_ = glm::vec3(output_ar / out_ar, 1.f, 1.f);
else
windows_[i].output_render_->scale_ = glm::vec3(1.f, out_ar / output_ar, 1.f);
// reset translation
windows_[i].output_render_->translation_ = glm::vec3(0.f);
// do not show output frame
windows_[i].output_frame_->visible_ = false;
}
}
output_ar = Mixer::manager().session()->frame()->aspectRatio(); output_ar = Mixer::manager().session()->frame()->aspectRatio();
} }
@@ -178,7 +222,7 @@ void DisplaysView::recenter ()
Surface *surf = new Surface( new Shader); Surface *surf = new Surface( new Shader);
surf->shader()->color = glm::vec4( 0.1f, 0.1f, 0.1f, 1.f ); surf->shader()->color = glm::vec4( 0.1f, 0.1f, 0.1f, 1.f );
m->attach(surf); m->attach(surf);
// cyan color frame // Monitor color frame
Frame *frame = new Frame(Frame::SHARP, Frame::THIN, Frame::GLOW); Frame *frame = new Frame(Frame::SHARP, Frame::THIN, Frame::GLOW);
frame->color = glm::vec4( COLOR_MONITOR, 1.f); frame->color = glm::vec4( COLOR_MONITOR, 1.f);
m->attach(frame); m->attach(frame);
@@ -255,29 +299,17 @@ void DisplaysView::draw()
int i = 0; int i = 0;
for (; i < Settings::application.num_output_windows; ++i) { for (; i < Settings::application.num_output_windows; ++i) {
// update visible flag // Render the output into the render buffer (displayed on the FrameBufferSurface surface_)
windows_[i].root_->visible_ = true; windows_[i].output_render_->update(0.f);
windows_[i].icon_->visible_ = Settings::application.render.disabled; windows_[i].renderbuffer_->begin();
windows_[i].output_render_->draw(glm::identity<glm::mat4>(), windows_[i].renderbuffer_->projection());
windows_[i].renderbuffer_->end();
if (windows_[i].render_->visible_) { // ensure the shader of the surface_ is configured
// rendering of framebuffer in window windows_[i].shader_->uniforms_["Red"] = Settings::application.windows[i+1].whitebalance.x;
if (Settings::application.windows[i+1].scaled) { windows_[i].shader_->uniforms_["Green"] = Settings::application.windows[i+1].whitebalance.y;
windows_[i].render_->scale_ = glm::vec3(1.f, 1.f, 1.f); windows_[i].shader_->uniforms_["Blue"] = Settings::application.windows[i+1].whitebalance.z;
} windows_[i].shader_->uniforms_["Temperature"] = Settings::application.windows[i+1].whitebalance.w;
else {
float out_ar = windows_[i].root_->scale_.x / windows_[i].root_->scale_.y;
if (output_ar < out_ar)
windows_[i].render_->scale_ = glm::vec3(output_ar / out_ar, 1.f, 1.f);
else
windows_[i].render_->scale_ = glm::vec3(1.f, out_ar / output_ar, 1.f);
}
if (windows_[i].shader_) {
windows_[i].shader_->uniforms_["Red"] = Settings::application.windows[i+1].whitebalance.x;
windows_[i].shader_->uniforms_["Green"] = Settings::application.windows[i+1].whitebalance.y;
windows_[i].shader_->uniforms_["Blue"] = Settings::application.windows[i+1].whitebalance.z;
windows_[i].shader_->uniforms_["Temperature"] = Settings::application.windows[i+1].whitebalance.w;
}
}
// update overlay // update overlay
if ( Settings::application.windows[i+1].fullscreen ) { if ( Settings::application.windows[i+1].fullscreen ) {
@@ -308,7 +340,6 @@ void DisplaysView::draw()
ImGui::End(); ImGui::End();
} }
} }
else { else {
// output overlay for window // output overlay for window
@@ -367,6 +398,9 @@ void DisplaysView::draw()
windows_[i].title_->scale_.y = WINDOW_TITLEBAR_HEIGHT / windows_[i].root_->scale_.y; windows_[i].title_->scale_.y = WINDOW_TITLEBAR_HEIGHT / windows_[i].root_->scale_.y;
windows_[i].title_->translation_.y = 1.f + windows_[i].title_->scale_.y; windows_[i].title_->translation_.y = 1.f + windows_[i].title_->scale_.y;
} }
windows_[i].output_frame_->scale_ = Settings::application.windows[i+1].scale;
windows_[i].output_frame_->translation_ = Settings::application.windows[i+1].translation;
} }
} }
@@ -405,7 +439,9 @@ void DisplaysView::draw()
// //
// Disable output // Disable output
ImGuiToolkit::ButtonToggle(ICON_FA_EYE_SLASH, &Settings::application.render.disabled, MENU_OUTPUTDISABLE); ImGuiToolkit::ButtonToggle(ICON_FA_EYE_SLASH, &Settings::application.render.disabled);
if (ImGui::IsItemHovered())
ImGuiToolkit::ToolTip(MENU_OUTPUTDISABLE, SHORTCUT_OUTPUTDISABLE);
// Add / Remove windows // Add / Remove windows
ImGui::SameLine(); ImGui::SameLine();
@@ -438,10 +474,10 @@ void DisplaysView::draw()
// Output options // Output options
ImGui::SameLine(0, 2.f * g.Style.FramePadding.x); ImGui::SameLine(0, 2.f * g.Style.FramePadding.x);
ImGuiToolkit::ButtonIconToggle(8,5,9,5, &Settings::application.windows[1+current_window_].scaled, "Window fit"); ImGuiToolkit::ButtonIconToggle(9,5,9,5, &Settings::application.windows[1+current_window_].scaled, "Custom fit");
ImGui::SameLine(0, g.Style.FramePadding.x); ImGui::SameLine(0, g.Style.FramePadding.x);
ImGuiToolkit::ButtonIconToggle(10,1,11,1, &Settings::application.windows[1+current_window_].show_pattern, "Test pattern"); ImGuiToolkit::ButtonIconToggle(11,1,11,1, &Settings::application.windows[1+current_window_].show_pattern, "Test pattern");
// White ballance color button // White ballance color button
static DialogToolkit::ColorPickerDialog whitebalancedialog; static DialogToolkit::ColorPickerDialog whitebalancedialog;
@@ -523,8 +559,8 @@ void DisplaysView::draw()
} }
if (ImGui::BeginPopup("DisplaysOutputContextMenu")) { if (ImGui::BeginPopup("DisplaysOutputContextMenu")) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_WINDOW, 1.f));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(COLOR_MENU_HOVERED, 0.5f)); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(COLOR_MENU_HOVERED, 0.5f));
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_WINDOW, 1.f));
// FULLSCREEN selection: list of monitors // FULLSCREEN selection: list of monitors
int index = 1; int index = 1;
@@ -555,25 +591,6 @@ void DisplaysView::draw()
Rendering::manager().outputWindow(current_window_).setDecoration(!_borderless); Rendering::manager().outputWindow(current_window_).setDecoration(!_borderless);
} }
if (ImGui::MenuItem( ICON_FA_EXPAND_ALT " Reset aspect ratio" , nullptr, false, _windowed )){
// reset aspect ratio
glm::ivec4 rect = windowCoordinates(current_window_);
float ar = Mixer::manager().session()->frame()->aspectRatio();
if ( rect.p / rect.q > ar)
rect.p = ar * rect.q;
else
rect.q = rect.p / ar;
Rendering::manager().outputWindow(current_window_).setCoordinates( rect );
}
if (ImGui::MenuItem( ICON_FA_RULER_COMBINED " Reset to pixel size", nullptr, false, _windowed )){
// reset resolution to 1:1
glm::ivec4 rect = windowCoordinates(current_window_);
rect.p = Mixer::manager().session()->frame()->width();
rect.q = Mixer::manager().session()->frame()->height();
Rendering::manager().outputWindow(current_window_).setCoordinates( rect );
}
if (ImGui::MenuItem( ICON_FA_EXPAND " Fit all Displays", nullptr, false, _windowed )){ if (ImGui::MenuItem( ICON_FA_EXPAND " Fit all Displays", nullptr, false, _windowed )){
Rendering::manager().outputWindow(current_window_).setDecoration(false); Rendering::manager().outputWindow(current_window_).setDecoration(false);
@@ -589,6 +606,25 @@ void DisplaysView::draw()
Rendering::manager().outputWindow(current_window_).setCoordinates( rect ); Rendering::manager().outputWindow(current_window_).setCoordinates( rect );
} }
if (ImGui::MenuItem( ICON_FA_EXPAND_ALT " Restore aspect ratio" , nullptr, false, _windowed )){
// reset aspect ratio
glm::ivec4 rect = windowCoordinates(current_window_);
float ar = Mixer::manager().session()->frame()->aspectRatio();
if ( rect.p / rect.q > ar)
rect.p = ar * rect.q;
else
rect.q = rect.p / ar;
Rendering::manager().outputWindow(current_window_).setCoordinates( rect );
}
if (ImGui::MenuItem( ICON_FA_RULER_COMBINED " Rescale to pixel size", nullptr, false, _windowed )){
// reset resolution to 1:1
glm::ivec4 rect = windowCoordinates(current_window_);
rect.p = Mixer::manager().session()->frame()->width();
rect.q = Mixer::manager().session()->frame()->height();
Rendering::manager().outputWindow(current_window_).setCoordinates( rect );
}
ImGui::Separator(); ImGui::Separator();
if ( ImGui::MenuItem( ICON_FA_REPLY " Reset") ) { if ( ImGui::MenuItem( ICON_FA_REPLY " Reset") ) {
glm::ivec4 rect (0, 0, 800, 600); glm::ivec4 rect (0, 0, 800, 600);
@@ -597,6 +633,8 @@ void DisplaysView::draw()
Rendering::manager().outputWindow(current_window_).setDecoration(true); Rendering::manager().outputWindow(current_window_).setDecoration(true);
Settings::application.windows[1+current_window_].show_pattern = false; Settings::application.windows[1+current_window_].show_pattern = false;
Settings::application.windows[1+current_window_].scaled = false; Settings::application.windows[1+current_window_].scaled = false;
Settings::application.windows[1+current_window_].scale = glm::vec3(1.f);
Settings::application.windows[1+current_window_].translation = glm::vec3(0.f);
Settings::application.windows[1+current_window_].whitebalance = glm::vec4(1.f, 1.f, 1.f, 0.5f); Settings::application.windows[1+current_window_].whitebalance = glm::vec4(1.f, 1.f, 1.f, 0.5f);
if (Settings::application.windows[current_window_+1].fullscreen) if (Settings::application.windows[current_window_+1].fullscreen)
Rendering::manager().outputWindow(current_window_).exitFullscreen(); Rendering::manager().outputWindow(current_window_).exitFullscreen();
@@ -604,6 +642,16 @@ void DisplaysView::draw()
Rendering::manager().outputWindow(current_window_).setCoordinates( rect ); Rendering::manager().outputWindow(current_window_).setCoordinates( rect );
} }
if ( Settings::application.windows[current_window_+1].scaled ) {
ImGui::PopStyleColor(1);
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_FRAME, 1.f));
if ( ImGui::MenuItem( ICON_FA_VECTOR_SQUARE " Reset custom fit") ) {
Settings::application.windows[1+current_window_].scale = glm::vec3(1.f);
Settings::application.windows[1+current_window_].translation = glm::vec3(0.f);
}
}
ImGui::PopStyleColor(2); ImGui::PopStyleColor(2);
ImGui::EndPopup(); ImGui::EndPopup();
} }
@@ -616,47 +664,39 @@ std::pair<Node *, glm::vec2> DisplaysView::pick(glm::vec2 P)
// prepare empty return value // prepare empty return value
std::pair<Node *, glm::vec2> pick = { nullptr, glm::vec2(0.f) }; std::pair<Node *, glm::vec2> pick = { nullptr, glm::vec2(0.f) };
// mode placement output window // get picking from generic View
if ( display_action_ == 0 ) { pick = View::pick(P);
// get picking from generic View
pick = View::pick(P);
// test all windows // test all windows
current_window_ = -1; current_window_ = -1;
for (int i = 0; i < Settings::application.num_output_windows; ++i) { for (int i = 0; i < Settings::application.num_output_windows; ++i) {
// ignore pick on render surface: it's the same as output surface // ignore pick on title: it's the same as output surface
if (pick.first == windows_[i].render_ || if (pick.first == windows_[i].title_ ||
pick.first == windows_[i].fullscreen_ || pick.first == windows_[i].fullscreen_ )
pick.first == windows_[i].title_ ) pick.first = windows_[i].surface_;
pick.first = windows_[i].surface_;
// detect clic on menu // detect clic on menu
if (pick.first == windows_[i].menu_) if (pick.first == windows_[i].menu_)
show_window_menu_ = true; show_window_menu_ = true;
// activate / deactivate window if clic on any element of it
if ( (pick.first == windows_[i].surface_) ||
(pick.first == windows_[i].handles_) ||
(pick.first == windows_[i].menu_) ) {
current_window_ = i;
windows_[i].overlays_->setActive(1);
}
else
windows_[i].overlays_->setActive(0);
// activate / deactivate window if clic on any element of it
if ( (pick.first == windows_[i].surface_) ||
(pick.first == windows_[i].handles_) ||
(pick.first == windows_[i].output_handles_) ||
(pick.first == windows_[i].menu_) ) {
current_window_ = i;
windows_[i].overlays_->setActive(1);
}
else {
windows_[i].overlays_->setActive(0);
} }
// ignore anything else than selected window
if (current_window_ < 0)
pick.first = nullptr;
} }
// mode other
else // if ( display_action_ == 1 )
{
} // ignore anything else than selected window
if (current_window_ < 0)
pick.first = nullptr;
return pick; return pick;
} }
@@ -672,36 +712,34 @@ void DisplaysView::select(glm::vec2 A, glm::vec2 B)
glm::vec3 scene_point_A = Rendering::manager().unProject(A); glm::vec3 scene_point_A = Rendering::manager().unProject(A);
glm::vec3 scene_point_B = Rendering::manager().unProject(B); glm::vec3 scene_point_B = Rendering::manager().unProject(B);
// select area in window placement mode // picking visitor traverses the scene
if ( display_action_ == 0 ) { PickingVisitor pv(scene_point_A, scene_point_B, true);
// picking visitor traverses the scene scene.accept(pv);
PickingVisitor pv(scene_point_A, scene_point_B, true);
scene.accept(pv);
// TODO Multiple window selection? // TODO Multiple window selection?
if (!pv.empty()) { if (!pv.empty()) {
// find which window was picked // find which window was picked
auto itp = pv.rbegin(); auto itp = pv.rbegin();
for (; itp != pv.rend(); ++itp){ for (; itp != pv.rend(); ++itp){
// search for WindowPreview // search for WindowPreview
auto w = std::find_if(windows_.begin(), windows_.end(), WindowPreview::hasNode(itp->first)); auto w = std::find_if(windows_.begin(), windows_.end(), WindowPreview::hasNode(itp->first));
if (w != windows_.end()) { if (w != windows_.end()) {
// cancel previous current // cancel previous current
if (current_window_>-1) if (current_window_>-1) {
windows_[current_window_].overlays_->setActive(0); windows_[current_window_].overlays_->setActive(0);
// set current windows_[current_window_].output_frame_->visible_ = false;
current_window_ = (int) std::distance(windows_.begin(), w);
windows_[current_window_].overlays_->setActive(1);
} }
// set current
current_window_ = (int) std::distance(windows_.begin(), w);
windows_[current_window_].overlays_->setActive(1);
windows_[current_window_].output_frame_->visible_ = true;
} }
} }
} }
} }
void DisplaysView::initiate() void DisplaysView::initiate()
@@ -709,8 +747,14 @@ void DisplaysView::initiate()
// initiate pending action // initiate pending action
if (!current_action_ongoing_ && current_window_ > -1) { if (!current_action_ongoing_ && current_window_ > -1) {
// store status // store status current window
// & make sure matrix transform of stored status is updated
current_window_status_->copyTransform(windows_[current_window_].root_); current_window_status_->copyTransform(windows_[current_window_].root_);
current_window_status_->update(0.f);
// store status current output frame in current window
current_output_status_->copyTransform(windows_[current_window_].output_frame_);
current_output_status_->update(0.f);
// initiated // initiated
current_action_ = ""; current_action_ = "";
@@ -735,6 +779,25 @@ void DisplaysView::terminate(bool force)
Rendering::manager().outputWindow(current_window_).setCoordinates( windowCoordinates(current_window_) ); Rendering::manager().outputWindow(current_window_).setCoordinates( windowCoordinates(current_window_) );
} }
// test if output area is inside the Window (with a margin of 10%)
GlmToolkit::AxisAlignedBoundingBox _bb;
_bb.extend(glm::vec3(-1.f, -1.f, 0.f));
_bb.extend(glm::vec3(1.f, 1.f, 0.f));
GlmToolkit::AxisAlignedBoundingBox output_bb = _bb.transformed( windows_[current_window_].output_frame_->transform_ );
_bb = _bb.scaled(glm::vec3(0.9f));
if ( !_bb.intersect(output_bb) || output_bb.area() < 0.1f ) {
// No intersection of output bounding box with window area : revert to previous
windows_[current_window_].output_frame_->scale_ = Settings::application.windows[current_window_+1].scale;
windows_[current_window_].output_frame_->translation_ = Settings::application.windows[current_window_+1].translation;
}
else {
// Apply output area recentering to actual output window
Settings::application.windows[current_window_+1].scale = windows_[current_window_].output_frame_->scale_;
Settings::application.windows[current_window_+1].translation = windows_[current_window_].output_frame_->translation_;
}
// reset overlay of grab corner
windows_[current_window_].output_handles_->overlayActiveCorner(glm::vec2(0.f));
} }
// terminated // terminated
@@ -774,8 +837,89 @@ View::Cursor DisplaysView::grab (Source *, glm::vec2 from, glm::vec2 to, std::pa
glm::vec3 scene_to = Rendering::manager().unProject(to, scene.root()->transform_); glm::vec3 scene_to = Rendering::manager().unProject(to, scene.root()->transform_);
glm::vec3 scene_translation = scene_to - scene_from; glm::vec3 scene_translation = scene_to - scene_from;
// a window is currently selected
if ( current_window_ > -1 ) { if ( current_window_ > -1 ) {
// Grab handles of the output frame to adjust
if ( pick.first == windows_[current_window_].output_handles_ ) {
// which corner was picked ?
glm::vec2 corner = glm::round(pick.second);
// inform on which corner should be overlayed (opposite)
windows_[current_window_].output_handles_->overlayActiveCorner(-corner);
// transform from center to corner
glm::mat4 T = GlmToolkit::transform(glm::vec3(corner.x, corner.y, 0.f), glm::vec3(0.f, 0.f, 0.f),
glm::vec3(1.f, 1.f, 1.f));
glm::mat4 root_to_corner_transform = T * glm::inverse(current_output_status_->transform_);
glm::mat4 corner_to_root_transform = glm::inverse(root_to_corner_transform);
// transformation from scene to corner:
glm::mat4 scene_to_corner_transform = root_to_corner_transform * glm::inverse( current_window_status_->transform_);
// compute cursor movement in corner reference frame
glm::vec4 corner_from = scene_to_corner_transform * glm::vec4( scene_from, 1.f );
glm::vec4 corner_to = scene_to_corner_transform * glm::vec4( scene_to, 1.f );
// operation of scaling in corner reference frame
glm::vec3 corner_scaling = glm::vec3(corner_to) / glm::vec3(corner_from);
// proportional SCALING with SHIFT
if (UserInterface::manager().shiftModifier()) {
// calculate proportional scaling factor
float factor = glm::length( glm::vec2( corner_to ) ) / glm::length( glm::vec2( corner_from ) );
// scale node
windows_[current_window_].output_frame_->scale_ = current_output_status_->scale_ * glm::vec3(factor, factor, 1.f);
}
// non-proportional CORNER RESIZE (normal case)
else {
// scale node
windows_[current_window_].output_frame_->scale_ = current_output_status_->scale_ * corner_scaling;
}
// update corner scaling to apply to center coordinates
corner_scaling = windows_[current_window_].output_frame_->scale_ / current_output_status_->scale_;
// TRANSLATION CORNER
// convert source position in corner reference frame
glm::vec4 center = root_to_corner_transform * glm::vec4( current_output_status_->translation_, 1.f);
// transform source center (in corner reference frame)
center = glm::scale(glm::identity<glm::mat4>(), corner_scaling) * center;
// convert center back into scene reference frame
center = corner_to_root_transform * center;
// apply to node
windows_[current_window_].output_frame_->translation_ = glm::vec3(center);
// discretized scaling with ALT
if (UserInterface::manager().altModifier()) {
windows_[current_window_].output_frame_->scale_ = glm::round( windows_[current_window_].output_frame_->scale_ * 20.f) * 0.05f;
windows_[current_window_].output_frame_->translation_ = glm::round( windows_[current_window_].output_frame_->translation_ * 20.f) * 0.05f;
}
if (corner.x > 0.f) {
if (corner.y > 0.f)
info << "Top Right";
else
info << "Bottom Right";
}
else {
if (corner.y > 0.f)
info << "Top Left";
else
info << "Bottom Left";
}
// show cursor depending on diagonal (corner picked)
T = glm::rotate(glm::identity<glm::mat4>(), current_output_status_->rotation_.z, glm::vec3(0.f, 0.f, 1.f));
T = glm::scale(T, current_output_status_->scale_);
corner = T * glm::vec4( corner, 0.f, 0.f );
ret.type = corner.x * corner.y > 0.f ? Cursor_ResizeNESW : Cursor_ResizeNWSE;
// info << "Output resized " << std::fixed << std::setprecision(1) << 100.f * windows_[current_window_].output_frame_->scale_.x;
// info << " x " << 100.f * windows_[current_window_].output_frame_->scale_.y << " %";
}
// grab window not fullscreen : move or resizes // grab window not fullscreen : move or resizes
if (!Settings::application.windows[current_window_+1].fullscreen) { if (!Settings::application.windows[current_window_+1].fullscreen) {
@@ -863,12 +1007,8 @@ View::Cursor DisplaysView::grab (Source *, glm::vec2 from, glm::vec2 to, std::pa
windows_[current_window_].title_->scale_.y = WINDOW_TITLEBAR_HEIGHT / windows_[current_window_].root_->scale_.y; windows_[current_window_].title_->scale_.y = WINDOW_TITLEBAR_HEIGHT / windows_[current_window_].root_->scale_.y;
windows_[current_window_].title_->translation_.y = 1.f + windows_[current_window_].title_->scale_.y; windows_[current_window_].title_->translation_.y = 1.f + windows_[current_window_].title_->scale_.y;
// show cursor depending on diagonal (corner picked) // show cursor
T = glm::rotate(glm::identity<glm::mat4>(), current_window_status_->rotation_.z, glm::vec3(0.f, 0.f, 1.f)); ret.type = Cursor_ResizeNWSE;
T = glm::scale(T, current_window_status_->scale_);
corner = T * glm::vec4( corner, 0.f, 0.f );
ret.type = corner.x * corner.y > 0.f ? Cursor_ResizeNESW : Cursor_ResizeNWSE;
rect = windowCoordinates(current_window_); rect = windowCoordinates(current_window_);
info << "Window size " << rect.p << " x " << rect.q << " px"; info << "Window size " << rect.p << " x " << rect.q << " px";
} }
@@ -1053,7 +1193,7 @@ bool WindowPreview::hasNode::operator()(WindowPreview elem) const
{ {
if (_n) if (_n)
{ {
if (_n == elem.render_ || if (_n == elem.fullscreen_ ||
_n == elem.surface_ || _n == elem.surface_ ||
_n == elem.title_) _n == elem.title_)
return true; return true;

View File

@@ -6,11 +6,13 @@
struct WindowPreview struct WindowPreview
{ {
Group *root_; class FrameBuffer *renderbuffer_;
Group *status_;
Surface *surface_;
Surface *render_;
class ImageFilteringShader *shader_; class ImageFilteringShader *shader_;
class FrameBufferSurface *surface_;
Surface *output_render_;
Group *root_;
Group *output_frame_;
Handles *output_handles_;
Switch *overlays_; Switch *overlays_;
Switch *mode_; Switch *mode_;
Handles *handles_; Handles *handles_;
@@ -21,12 +23,14 @@ struct WindowPreview
std::string monitor_; std::string monitor_;
WindowPreview() { WindowPreview() {
root_ = nullptr; renderbuffer_ = nullptr;
status_ = nullptr; shader_ = nullptr;
surface_ = nullptr; surface_ = nullptr;
render_ = nullptr; output_render_ = nullptr;
root_ = nullptr;
output_frame_ = nullptr;
output_handles_ = nullptr;
overlays_ = nullptr; overlays_ = nullptr;
status_ = nullptr;
mode_ = nullptr; mode_ = nullptr;
handles_ = nullptr; handles_ = nullptr;
menu_ = nullptr; menu_ = nullptr;
@@ -81,10 +85,9 @@ private:
std::vector<WindowPreview> windows_; std::vector<WindowPreview> windows_;
int current_window_; int current_window_;
Group *current_window_status_; Group *current_window_status_;
Group *current_output_status_;
bool show_window_menu_; bool show_window_menu_;
int display_action_;
// bool get_UV_window_render_from_pick(const glm::vec3 &pos, glm::vec2 *uv); // bool get_UV_window_render_from_pick(const glm::vec3 &pos, glm::vec2 *uv);

View File

@@ -238,6 +238,14 @@ GlmToolkit::AxisAlignedBoundingBox GlmToolkit::AxisAlignedBoundingBox::transform
return bb; return bb;
} }
float GlmToolkit::AxisAlignedBoundingBox::area() const
{
if (isNull())
return 0.f;
return (mMax.x - mMin.x) * (mMax.y - mMin.y) ;
}
bool GlmToolkit::operator< (const GlmToolkit::AxisAlignedBoundingBox& A, const GlmToolkit::AxisAlignedBoundingBox& B ) bool GlmToolkit::operator< (const GlmToolkit::AxisAlignedBoundingBox& A, const GlmToolkit::AxisAlignedBoundingBox& B )
{ {
if (A.isNull()) if (A.isNull())

View File

@@ -24,6 +24,7 @@ public:
inline glm::vec3 max() const { return mMax; } inline glm::vec3 max() const { return mMax; }
glm::vec3 center(bool ignore_z = true) const; glm::vec3 center(bool ignore_z = true) const;
glm::vec3 scale(bool ignore_z = true) const; glm::vec3 scale(bool ignore_z = true) const;
float area() const;
bool intersect(const AxisAlignedBoundingBox& bb, bool ignore_z = true) const; bool intersect(const AxisAlignedBoundingBox& bb, bool ignore_z = true) const;
bool contains(const AxisAlignedBoundingBox& bb, bool ignore_z = true) const; bool contains(const AxisAlignedBoundingBox& bb, bool ignore_z = true) const;
bool contains(glm::vec3 point, bool ignore_z = true) const; bool contains(glm::vec3 point, bool ignore_z = true) const;

View File

@@ -1133,17 +1133,20 @@ bool RenderingWindow::draw(FrameBuffer *fb)
shader_->uniforms_["Temperature"] = Settings::application.windows[index_].whitebalance.w; shader_->uniforms_["Temperature"] = Settings::application.windows[index_].whitebalance.w;
} }
// calculate scaling factor of frame buffer inside window
const float windowAspectRatio = aspectRatio();
const float renderingAspectRatio = fb->aspectRatio();
glm::vec3 scale = glm::vec3(1.f, 1.f, 1.f);
// Display option: scaled or corrected aspect ratio // Display option: scaled or corrected aspect ratio
if (!Settings::application.windows[index_].scaled) { if (Settings::application.windows[index_].scaled) {
surface_->scale_ = Settings::application.windows[index_].scale;
surface_->translation_ = Settings::application.windows[index_].translation;
}
else{
// calculate scaling factor of frame buffer inside window
const float windowAspectRatio = aspectRatio();
const float renderingAspectRatio = fb->aspectRatio();
if (windowAspectRatio < renderingAspectRatio) if (windowAspectRatio < renderingAspectRatio)
scale = glm::vec3(1.f, windowAspectRatio / renderingAspectRatio, 1.f); surface_->scale_ = glm::vec3(1.f, windowAspectRatio / renderingAspectRatio, 1.f);
else else
scale = glm::vec3(renderingAspectRatio / windowAspectRatio, 1.f, 1.f); surface_->scale_ = glm::vec3(renderingAspectRatio / windowAspectRatio, 1.f, 1.f);
surface_->translation_ = glm::vec3(0.f);
} }
// Display option: draw calibration pattern // Display option: draw calibration pattern
@@ -1168,7 +1171,8 @@ bool RenderingWindow::draw(FrameBuffer *fb)
// actual render of the textured surface // actual render of the textured surface
glBindTexture(GL_TEXTURE_2D, textureid_); glBindTexture(GL_TEXTURE_2D, textureid_);
static glm::mat4 projection = glm::ortho(-1.f, 1.f, -1.f, 1.f, -1.f, 1.f); static glm::mat4 projection = glm::ortho(-1.f, 1.f, -1.f, 1.f, -1.f, 1.f);
surface_->draw(glm::scale(glm::identity<glm::mat4>(), scale), projection); surface_->update(0.f);
surface_->draw(glm::identity<glm::mat4>(), projection);
// done drawing (unload shader from this glcontext) // done drawing (unload shader from this glcontext)
ShadingProgram::enduse(); ShadingProgram::enduse();

View File

@@ -110,8 +110,16 @@ void Settings::Save(uint64_t runtime)
window->SetAttribute("f", w.fullscreen); window->SetAttribute("f", w.fullscreen);
window->SetAttribute("s", w.scaled); window->SetAttribute("s", w.scaled);
window->SetAttribute("d", w.decorated); window->SetAttribute("d", w.decorated);
window->SetAttribute("m", w.monitor.c_str()); window->SetAttribute("m", w.monitor.c_str());
window->InsertEndChild( XMLElementFromGLM(&xmlDoc, w.whitebalance) ); XMLElement *tmp = xmlDoc.NewElement("whitebalance");
tmp->InsertEndChild( XMLElementFromGLM(&xmlDoc, w.whitebalance) );
window->InsertEndChild( tmp );
tmp = xmlDoc.NewElement("scale");
tmp->InsertEndChild( XMLElementFromGLM(&xmlDoc, w.scale) );
window->InsertEndChild( tmp );
tmp = xmlDoc.NewElement("translation");
tmp->InsertEndChild( XMLElementFromGLM(&xmlDoc, w.translation) );
window->InsertEndChild( tmp );
windowsNode->InsertEndChild(window); windowsNode->InsertEndChild(window);
} }
@@ -515,11 +523,19 @@ void Settings::Load()
int i = 0; int i = 0;
windowNode->QueryIntAttribute("id", &i); windowNode->QueryIntAttribute("id", &i);
if (i > 0) if (i > 0)
w.name = APP_NAME " output " + std::to_string(i); w.name = "Output " + std::to_string(i) + " - " APP_NAME;
else else
w.name = APP_TITLE; w.name = APP_TITLE;
tinyxml2::XMLElementToGLM( windowNode->FirstChildElement("vec4"), w.whitebalance); XMLElement *tmp = windowNode->FirstChildElement("whitebalance");
if (tmp)
tinyxml2::XMLElementToGLM( tmp->FirstChildElement("vec4"), w.whitebalance);
tmp = windowNode->FirstChildElement("scale");
if (tmp)
tinyxml2::XMLElementToGLM( tmp->FirstChildElement("vec3"), w.scale);
tmp = windowNode->FirstChildElement("translation");
if (tmp)
tinyxml2::XMLElementToGLM( tmp->FirstChildElement("vec3"), w.translation);
application.windows[i] = w; application.windows[i] = w;
} }
@@ -549,11 +565,13 @@ void Settings::Load()
application.views[id].name = viewNode->Attribute("name"); application.views[id].name = viewNode->Attribute("name");
XMLElement* scaleNode = viewNode->FirstChildElement("default_scale"); XMLElement* scaleNode = viewNode->FirstChildElement("default_scale");
tinyxml2::XMLElementToGLM( scaleNode->FirstChildElement("vec3"), if (scaleNode)
tinyxml2::XMLElementToGLM( scaleNode->FirstChildElement("vec3"),
application.views[id].default_scale); application.views[id].default_scale);
XMLElement* translationNode = viewNode->FirstChildElement("default_translation"); XMLElement* translationNode = viewNode->FirstChildElement("default_translation");
tinyxml2::XMLElementToGLM( translationNode->FirstChildElement("vec3"), if (translationNode)
tinyxml2::XMLElementToGLM( translationNode->FirstChildElement("vec3"),
application.views[id].default_translation); application.views[id].default_translation);
} }

View File

@@ -65,10 +65,13 @@ struct WindowConfig
std::string monitor; std::string monitor;
bool show_pattern; bool show_pattern;
glm::vec4 whitebalance; glm::vec4 whitebalance;
glm::vec3 scale;
glm::vec3 translation;
WindowConfig() : name(APP_TITLE), x(15), y(15), w(1280), h(720), WindowConfig() : name(APP_TITLE), x(15), y(15), w(1280), h(720),
fullscreen(false), scaled(false), decorated(true), fullscreen(false), scaled(false), decorated(true),
monitor(""), show_pattern(false), whitebalance(glm::vec4(1.f, 1.f, 1.f, 0.5f)) monitor(""), show_pattern(false), whitebalance(glm::vec4(1.f, 1.f, 1.f, 0.5f)),
scale(glm::vec3(1.f)), translation(glm::vec3(0.f))
{ } { }
}; };