mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 23:40:02 +01:00
Finally found how to improve blending modes by pre-multiplying color by alpha in the shader, so that the blending equations can be applied on top of the apha manipulation.
3483 lines
144 KiB
C++
3483 lines
144 KiB
C++
// Opengl
|
|
#include <glad/glad.h>
|
|
#include <glm/glm.hpp>
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
#include <glm/gtc/matrix_access.hpp>
|
|
#include <glm/gtx/vector_angle.hpp>
|
|
|
|
|
|
#include "imgui.h"
|
|
#include "ImGuiToolkit.h"
|
|
|
|
// memmove
|
|
#include <string.h>
|
|
#include <sstream>
|
|
#include <regex>
|
|
#include <iomanip>
|
|
|
|
#include "View.h"
|
|
#include "Mixer.h"
|
|
#include "defines.h"
|
|
#include "Settings.h"
|
|
#include "Session.h"
|
|
#include "Resource.h"
|
|
#include "Source.h"
|
|
#include "SessionSource.h"
|
|
#include "PickingVisitor.h"
|
|
#include "BoundingBoxVisitor.h"
|
|
#include "DrawVisitor.h"
|
|
#include "Decorations.h"
|
|
#include "Mixer.h"
|
|
#include "UserInterfaceManager.h"
|
|
#include "UpdateCallback.h"
|
|
#include "ActionManager.h"
|
|
#include "Log.h"
|
|
|
|
|
|
uint View::need_deep_update_ = 1;
|
|
|
|
View::View(Mode m) : mode_(m)
|
|
{
|
|
show_context_menu_ = false;
|
|
}
|
|
|
|
void View::restoreSettings()
|
|
{
|
|
scene.root()->scale_ = Settings::application.views[mode_].default_scale;
|
|
scene.root()->translation_ = Settings::application.views[mode_].default_translation;
|
|
}
|
|
|
|
void View::saveSettings()
|
|
{
|
|
Settings::application.views[mode_].default_scale = scene.root()->scale_;
|
|
Settings::application.views[mode_].default_translation = scene.root()->translation_;
|
|
}
|
|
|
|
void View::draw()
|
|
{
|
|
// draw scene of this view
|
|
scene.root()->draw(glm::identity<glm::mat4>(), Rendering::manager().Projection());
|
|
}
|
|
|
|
void View::update(float dt)
|
|
{
|
|
// recursive update from root of scene
|
|
scene.update( dt );
|
|
|
|
// a more complete update is requested
|
|
if (View::need_deep_update_ > 0) {
|
|
// reorder sources
|
|
scene.ws()->sort();
|
|
}
|
|
}
|
|
|
|
|
|
View::Cursor View::drag (glm::vec2 from, glm::vec2 to)
|
|
{
|
|
static glm::vec3 start_translation = glm::vec3(0.f);
|
|
static glm::vec2 start_position = glm::vec2(0.f);
|
|
|
|
if ( start_position != from ) {
|
|
start_position = from;
|
|
start_translation = scene.root()->translation_;
|
|
}
|
|
|
|
// unproject
|
|
glm::vec3 gl_Position_from = Rendering::manager().unProject(from);
|
|
glm::vec3 gl_Position_to = Rendering::manager().unProject(to);
|
|
|
|
// compute delta translation
|
|
scene.root()->translation_ = start_translation + gl_Position_to - gl_Position_from;
|
|
|
|
// apply and clamp
|
|
zoom(0.f);
|
|
|
|
return Cursor(Cursor_ResizeAll);
|
|
}
|
|
|
|
std::pair<Node *, glm::vec2> View::pick(glm::vec2 P)
|
|
{
|
|
// prepare empty return value
|
|
std::pair<Node *, glm::vec2> 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()) {
|
|
// select top-most Node picked
|
|
pick = pv.back();
|
|
}
|
|
|
|
return pick;
|
|
}
|
|
|
|
void View::initiate()
|
|
{
|
|
current_action_ = "";
|
|
current_id_ = 0;
|
|
for (auto sit = Mixer::manager().session()->begin();
|
|
sit != Mixer::manager().session()->end(); sit++){
|
|
|
|
(*sit)->stored_status_->copyTransform((*sit)->group(mode_));
|
|
}
|
|
}
|
|
|
|
void View::terminate()
|
|
{
|
|
std::regex r("\\n");
|
|
current_action_ = std::regex_replace(current_action_, r, " ");
|
|
Action::manager().store(current_action_, current_id_);
|
|
current_action_ = "";
|
|
current_id_ = 0;
|
|
}
|
|
|
|
void View::zoom( float factor )
|
|
{
|
|
resize( size() + int(factor * 2.f));
|
|
}
|
|
|
|
void View::recenter()
|
|
{
|
|
// restore default view
|
|
restoreSettings();
|
|
|
|
// nothing else if scene is empty
|
|
if (scene.ws()->numChildren() < 1)
|
|
return;
|
|
|
|
// calculate screen area visible in the default view
|
|
GlmToolkit::AxisAlignedBoundingBox view_box;
|
|
glm::mat4 modelview = GlmToolkit::transform(scene.root()->translation_, scene.root()->rotation_, scene.root()->scale_);
|
|
view_box.extend( Rendering::manager().unProject(glm::vec2(0.f, Rendering::manager().mainWindow().height()), modelview) );
|
|
view_box.extend( Rendering::manager().unProject(glm::vec2(Rendering::manager().mainWindow().width(), 0.f), modelview) );
|
|
|
|
// calculate screen area required to see the entire scene
|
|
BoundingBoxVisitor scene_visitor_bbox;
|
|
scene.accept(scene_visitor_bbox);
|
|
GlmToolkit::AxisAlignedBoundingBox scene_box = scene_visitor_bbox.bbox();
|
|
|
|
// if the default view does not contains the entire scene
|
|
// we shall adjust the view to fit the scene
|
|
if ( !view_box.contains(scene_box)) {
|
|
|
|
// drag view to move towards scene_box center (while remaining in limits of the view)
|
|
glm::vec2 from = Rendering::manager().project(-view_box.center(), modelview);
|
|
glm::vec2 to = Rendering::manager().project(-scene_box.center(), modelview);
|
|
drag(from, to);
|
|
|
|
// recalculate the view bounding box
|
|
GlmToolkit::AxisAlignedBoundingBox updated_view_box;
|
|
modelview = GlmToolkit::transform(scene.root()->translation_, scene.root()->rotation_, scene.root()->scale_);
|
|
updated_view_box.extend( Rendering::manager().unProject(glm::vec2(0.f, Rendering::manager().mainWindow().height()), modelview) );
|
|
updated_view_box.extend( Rendering::manager().unProject(glm::vec2(Rendering::manager().mainWindow().width(), 0.f), modelview) );
|
|
|
|
// 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)) {
|
|
|
|
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);
|
|
scene.root()->scale_.x = z;
|
|
scene.root()->scale_.y = z;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void View::selectAll()
|
|
{
|
|
Mixer::selection().clear();
|
|
for(auto sit = Mixer::manager().session()->begin();
|
|
sit != Mixer::manager().session()->end(); sit++) {
|
|
if (canSelect(*sit))
|
|
Mixer::selection().add(*sit);
|
|
}
|
|
// special case of one single source in selection : make current after release
|
|
if (Mixer::selection().size() == 1)
|
|
Mixer::manager().setCurrentSource( Mixer::selection().front() );
|
|
}
|
|
|
|
void View::select(glm::vec2 A, glm::vec2 B)
|
|
{
|
|
// unproject mouse coordinate into scene coordinates
|
|
glm::vec3 scene_point_A = Rendering::manager().unProject(A);
|
|
glm::vec3 scene_point_B = Rendering::manager().unProject(B);
|
|
|
|
// picking visitor traverses the scene
|
|
PickingVisitor pv(scene_point_A, scene_point_B);
|
|
scene.accept(pv);
|
|
|
|
// picking visitor found nodes in the area?
|
|
if ( !pv.empty()) {
|
|
|
|
// create a list of source matching the list of picked nodes
|
|
SourceList selection;
|
|
// loop over the nodes and add all sources found.
|
|
for(std::vector< std::pair<Node *, glm::vec2> >::const_reverse_iterator p = pv.rbegin(); p != pv.rend(); p++){
|
|
Source *s = Mixer::manager().findSource( p->first );
|
|
if (canSelect(s))
|
|
selection.push_back( s );
|
|
}
|
|
// set the selection with list of picked (overlaped) sources
|
|
Mixer::selection().set(selection);
|
|
}
|
|
else
|
|
// reset selection
|
|
Mixer::selection().clear();
|
|
}
|
|
|
|
bool View::canSelect(Source *s) {
|
|
|
|
return ( s!=nullptr && !s->locked() );
|
|
}
|
|
|
|
|
|
MixingView::MixingView() : View(MIXING), limbo_scale_(1.3f)
|
|
{
|
|
// read default settings
|
|
if ( Settings::application.views[mode_].name.empty() ) {
|
|
// no settings found: store application default
|
|
Settings::application.views[mode_].name = "Mixing";
|
|
scene.root()->scale_ = glm::vec3(MIXING_DEFAULT_SCALE, MIXING_DEFAULT_SCALE, 1.0f);
|
|
scene.root()->translation_ = glm::vec3(0.0f, 0.0f, 0.0f);
|
|
saveSettings();
|
|
}
|
|
else
|
|
restoreSettings();
|
|
|
|
// Mixing scene background
|
|
Mesh *tmp = new Mesh("mesh/disk.ply");
|
|
tmp->scale_ = glm::vec3(limbo_scale_, limbo_scale_, 1.f);
|
|
tmp->shader()->color = glm::vec4( COLOR_LIMBO_CIRCLE, 0.7f );
|
|
scene.bg()->attach(tmp);
|
|
|
|
mixingCircle_ = new Mesh("mesh/disk.ply");
|
|
mixingCircle_->setTexture(textureMixingQuadratic());
|
|
mixingCircle_->shader()->color = glm::vec4( 1.f, 1.f, 1.f, 1.f );
|
|
scene.bg()->attach(mixingCircle_);
|
|
|
|
circle_ = new Mesh("mesh/circle.ply");
|
|
circle_->shader()->color = glm::vec4( COLOR_CIRCLE, 0.9f );
|
|
scene.bg()->attach(circle_);
|
|
|
|
// Mixing scene foreground
|
|
tmp = new Mesh("mesh/disk.ply");
|
|
tmp->scale_ = glm::vec3(0.033f, 0.033f, 1.f);
|
|
tmp->translation_ = glm::vec3(0.f, 1.f, 0.f);
|
|
tmp->shader()->color = glm::vec4( COLOR_CIRCLE, 0.9f );
|
|
scene.fg()->attach(tmp);
|
|
|
|
button_white_ = new Disk();
|
|
button_white_->scale_ = glm::vec3(0.026f, 0.026f, 1.f);
|
|
button_white_->translation_ = glm::vec3(0.f, 1.f, 0.f);
|
|
button_white_->color = glm::vec4( 0.85f, 0.85f, 0.85f, 1.0f );
|
|
scene.fg()->attach(button_white_);
|
|
|
|
tmp = new Mesh("mesh/disk.ply");
|
|
tmp->scale_ = glm::vec3(0.033f, 0.033f, 1.f);
|
|
tmp->translation_ = glm::vec3(0.f, -1.f, 0.f);
|
|
tmp->shader()->color = glm::vec4( COLOR_CIRCLE, 0.9f );
|
|
scene.fg()->attach(tmp);
|
|
|
|
button_black_ = new Disk();
|
|
button_black_->scale_ = glm::vec3(0.026f, 0.026f, 1.f);
|
|
button_black_->translation_ = glm::vec3(0.f, -1.f, 0.f);
|
|
button_black_->color = glm::vec4( 0.1f, 0.1f, 0.1f, 1.0f );
|
|
scene.fg()->attach(button_black_);
|
|
|
|
slider_root_ = new Group;
|
|
scene.fg()->attach(slider_root_);
|
|
|
|
tmp = new Mesh("mesh/disk.ply");
|
|
tmp->scale_ = glm::vec3(0.08f, 0.08f, 1.f);
|
|
tmp->translation_ = glm::vec3(0.0f, 1.0f, 0.f);
|
|
tmp->shader()->color = glm::vec4( COLOR_CIRCLE, 0.9f );
|
|
slider_root_->attach(tmp);
|
|
|
|
slider_ = new Disk();
|
|
slider_->scale_ = glm::vec3(0.075f, 0.075f, 1.f);
|
|
slider_->translation_ = glm::vec3(0.0f, 1.0f, 0.f);
|
|
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);
|
|
stashCircle_->color = glm::vec4( COLOR_STASH_CIRCLE, 0.6f );
|
|
// scene.bg()->attach(stashCircle_);
|
|
}
|
|
|
|
|
|
void MixingView::draw()
|
|
{
|
|
// temporarily force shaders to use opacity blending for rendering icons
|
|
Shader::force_blending_opacity = true;
|
|
// draw scene of this view
|
|
View::draw();
|
|
// restore state
|
|
Shader::force_blending_opacity = false;
|
|
}
|
|
|
|
void MixingView::resize ( int scale )
|
|
{
|
|
float z = CLAMP(0.01f * (float) scale, 0.f, 1.f);
|
|
z *= z;
|
|
z *= MIXING_MAX_SCALE - MIXING_MIN_SCALE;
|
|
z += MIXING_MIN_SCALE;
|
|
scene.root()->scale_.x = z;
|
|
scene.root()->scale_.y = z;
|
|
|
|
// Clamp translation to acceptable area
|
|
glm::vec3 border(scene.root()->scale_.x * 1.f, scene.root()->scale_.y * 1.f, 0.f);
|
|
scene.root()->translation_ = glm::clamp(scene.root()->translation_, -border, border);
|
|
}
|
|
|
|
int MixingView::size ()
|
|
{
|
|
float z = (scene.root()->scale_.x - MIXING_MIN_SCALE) / (MIXING_MAX_SCALE - MIXING_MIN_SCALE);
|
|
return (int) ( sqrt(z) * 100.f);
|
|
}
|
|
|
|
void MixingView::centerSource(Source *s)
|
|
{
|
|
// setup view so that the center of the source ends at screen coordinates (650, 150)
|
|
// -> this is just next to the navigation pannel
|
|
glm::vec2 screenpoint = glm::vec2(500.f, 20.f) * Rendering::manager().mainWindow().dpiScale();
|
|
glm::vec3 pos_to = Rendering::manager().unProject(screenpoint, scene.root()->transform_);
|
|
glm::vec3 pos_from( - s->group(View::MIXING)->scale_.x, s->group(View::MIXING)->scale_.y, 0.f);
|
|
pos_from += s->group(View::MIXING)->translation_;
|
|
glm::vec4 pos_delta = glm::vec4(pos_to.x, pos_to.y, 0.f, 0.f) - glm::vec4(pos_from.x, pos_from.y, 0.f, 0.f);
|
|
pos_delta = scene.root()->transform_ * pos_delta;
|
|
scene.root()->translation_ += glm::vec3(pos_delta);
|
|
|
|
}
|
|
|
|
void MixingView::update(float dt)
|
|
{
|
|
View::update(dt);
|
|
|
|
// a more complete update is requested
|
|
// for mixing, this means restore position of the fading slider
|
|
if (View::need_deep_update_ > 0) {
|
|
|
|
//
|
|
// Set slider to match the actual fading of the session
|
|
//
|
|
float f = Mixer::manager().session()->empty() ? 0.f : Mixer::manager().session()->fading();
|
|
|
|
// reverse calculate angle from fading & move slider
|
|
slider_root_->rotation_.z = SIGN(slider_root_->rotation_.z) * asin(f) * 2.f;
|
|
|
|
// visual feedback on mixing circle
|
|
f = 1.f - f;
|
|
mixingCircle_->shader()->color = glm::vec4(f, f, f, 1.f);
|
|
|
|
}
|
|
// the current view is the mixing view
|
|
else if (Mixer::manager().view() == this )
|
|
{
|
|
//
|
|
// Set session fading to match the slider angle (during animation)
|
|
//
|
|
|
|
// calculate fading from angle
|
|
float f = sin( ABS(slider_root_->rotation_.z) * 0.5f);
|
|
|
|
// apply fading
|
|
if ( ABS_DIFF( f, Mixer::manager().session()->fading()) > EPSILON )
|
|
{
|
|
// apply fading to session
|
|
Mixer::manager().session()->setFading(f);
|
|
|
|
// visual feedback on mixing circle
|
|
f = 1.f - f;
|
|
mixingCircle_->shader()->color = glm::vec4(f, f, f, 1.f);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
std::pair<Node *, glm::vec2> MixingView::pick(glm::vec2 P)
|
|
{
|
|
// get picking from generic View
|
|
std::pair<Node *, glm::vec2> pick = View::pick(P);
|
|
|
|
// deal with internal interactive objects
|
|
if ( pick.first == button_white_ || pick.first == button_black_ ) {
|
|
|
|
RotateToCallback *anim = nullptr;
|
|
if (pick.first == button_white_)
|
|
anim = new RotateToCallback(0.f, 500.f);
|
|
else
|
|
anim = new RotateToCallback(SIGN(slider_root_->rotation_.z) * M_PI, 500.f);
|
|
|
|
// animate clic
|
|
pick.first->update_callbacks_.push_back(new BounceScaleCallback(0.3f));
|
|
|
|
// reset & start animation
|
|
slider_root_->update_callbacks_.clear();
|
|
slider_root_->update_callbacks_.push_back(anim);
|
|
|
|
}
|
|
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) };
|
|
}
|
|
}
|
|
|
|
return pick;
|
|
}
|
|
|
|
|
|
|
|
View::Cursor MixingView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
|
|
{
|
|
// unproject
|
|
glm::vec3 gl_Position_from = Rendering::manager().unProject(from, scene.root()->transform_);
|
|
glm::vec3 gl_Position_to = Rendering::manager().unProject(to, scene.root()->transform_);
|
|
|
|
// No source is given
|
|
if (!s) {
|
|
|
|
// if interaction with slider
|
|
if (pick.first == slider_) {
|
|
|
|
// apply rotation to match angle with mouse cursor
|
|
float angle = glm::orientedAngle( glm::normalize(glm::vec2(0.f, 1.0)), glm::normalize(glm::vec2(gl_Position_to)));
|
|
|
|
// snap on 0 and PI angles
|
|
if ( ABS_DIFF(angle, 0.f) < 0.05)
|
|
angle = 0.f;
|
|
else if ( ABS_DIFF(angle, M_PI) < 0.05)
|
|
angle = M_PI;
|
|
|
|
// animate slider (rotation angle on its parent)
|
|
slider_root_->rotation_.z = angle;
|
|
|
|
// cursor feedback
|
|
std::ostringstream info;
|
|
info << "Global opacity " << 100 - int(Mixer::manager().session()->fading() * 100.0) << " %";
|
|
return Cursor(Cursor_Hand, info.str() );
|
|
}
|
|
|
|
// nothing to do
|
|
return Cursor();
|
|
}
|
|
//
|
|
// Interaction with source
|
|
//
|
|
// compute delta translation
|
|
s->group(mode_)->translation_ = s->stored_status_->translation_ + gl_Position_to - gl_Position_from;
|
|
|
|
// // diagonal translation with SHIFT
|
|
// if (UserInterface::manager().shiftModifier()) {
|
|
// s->group(mode_)->translation_.y = s->group(mode_)->translation_.x * s->stored_status_->translation_.y / s->stored_status_->translation_.x;
|
|
// }
|
|
|
|
|
|
// // trying to enter stash
|
|
// if ( glm::distance( glm::vec2(s->group(mode_)->translation_), glm::vec2(stashCircle_->translation_)) < stashCircle_->scale_.x) {
|
|
|
|
// // refuse to put an active source in stash
|
|
// if (s->active())
|
|
// s->group(mode_)->translation_ = s->stored_status_->translation_;
|
|
// else {
|
|
// Mixer::manager().conceal(s);
|
|
// s->group(mode_)->scale_ = glm::vec3(MIXING_ICON_SCALE) - glm::vec3(0.1f, 0.1f, 0.f);
|
|
// }
|
|
// }
|
|
// else if ( Mixer::manager().concealed(s) ) {
|
|
// Mixer::manager().uncover(s);
|
|
// s->group(mode_)->scale_ = glm::vec3(MIXING_ICON_SCALE);
|
|
// }
|
|
|
|
// request update
|
|
s->touch();
|
|
|
|
std::ostringstream info;
|
|
if (s->active()) {
|
|
info << "Alpha " << std::fixed << std::setprecision(3) << s->blendingShader()->color.a << " ";
|
|
info << ( (s->blendingShader()->color.a > 0.f) ? ICON_FA_EYE : ICON_FA_EYE_SLASH);
|
|
}
|
|
else
|
|
info << "Inactive " << ICON_FA_SNOWFLAKE;
|
|
|
|
// store action in history
|
|
current_action_ = s->name() + ": " + info.str();
|
|
current_id_ = s->id();
|
|
|
|
return Cursor(Cursor_ResizeAll, info.str() );
|
|
}
|
|
|
|
void MixingView::arrow (glm::vec2 movement)
|
|
{
|
|
Source *s = Mixer::manager().currentSource();
|
|
if (s) {
|
|
|
|
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;
|
|
|
|
Group *sourceNode = s->group(mode_);
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->translation_ += glm::vec3(movement.x, -movement.y, 0.f) * 0.1f;
|
|
sourceNode->translation_.x = ROUND(sourceNode->translation_.x, 10.f);
|
|
sourceNode->translation_.y = ROUND(sourceNode->translation_.y, 10.f);
|
|
}
|
|
else
|
|
sourceNode->translation_ += gl_delta * ARROWS_MOVEMENT_FACTOR;
|
|
|
|
// request update
|
|
s->touch();
|
|
}
|
|
}
|
|
|
|
void MixingView::setAlpha(Source *s)
|
|
{
|
|
if (!s)
|
|
return;
|
|
|
|
// move the layer node of the source
|
|
Group *sourceNode = s->group(mode_);
|
|
glm::vec2 mix_pos = glm::vec2(DEFAULT_MIXING_TRANSLATION);
|
|
|
|
for(NodeSet::iterator it = scene.ws()->begin(); it != scene.ws()->end(); it++) {
|
|
|
|
if ( glm::distance(glm::vec2((*it)->translation_), mix_pos) < 0.001) {
|
|
mix_pos += glm::vec2(-0.03, 0.03);
|
|
}
|
|
}
|
|
|
|
sourceNode->translation_.x = mix_pos.x;
|
|
sourceNode->translation_.y = mix_pos.y;
|
|
|
|
// request update
|
|
s->touch();
|
|
}
|
|
|
|
#define CIRCLE_PIXELS 64
|
|
#define CIRCLE_PIXEL_RADIUS 1024.0
|
|
//#define CIRCLE_PIXELS 256
|
|
//#define CIRCLE_PIXEL_RADIUS 16384.0
|
|
//#define CIRCLE_PIXELS 1024
|
|
//#define CIRCLE_PIXEL_RADIUS 262144.0
|
|
|
|
float sin_quad_texture(float x, float y) {
|
|
// return 0.5f + 0.5f * cos( M_PI * CLAMP( ( ( x * x ) + ( y * y ) ) / CIRCLE_PIXEL_RADIUS, 0.f, 1.f ) );
|
|
float D = sqrt( ( x * x )/ CIRCLE_PIXEL_RADIUS + ( y * y )/ CIRCLE_PIXEL_RADIUS );
|
|
return 0.5f + 0.5f * cos( M_PI * CLAMP( D * sqrt(D), 0.f, 1.f ) );
|
|
}
|
|
|
|
uint MixingView::textureMixingQuadratic()
|
|
{
|
|
static GLuint texid = 0;
|
|
if (texid == 0) {
|
|
// generate the texture with alpha exactly as computed for sources
|
|
GLubyte matrix[CIRCLE_PIXELS*CIRCLE_PIXELS * 4];
|
|
GLubyte color[4] = {0,0,0,0};
|
|
GLfloat luminance = 1.f;
|
|
GLfloat alpha = 0.f;
|
|
GLfloat distance = 0.f;
|
|
int l = -CIRCLE_PIXELS / 2 + 1, c = 0;
|
|
|
|
for (int i = 0; i < CIRCLE_PIXELS / 2; ++i) {
|
|
c = -CIRCLE_PIXELS / 2 + 1;
|
|
for (int j=0; j < CIRCLE_PIXELS / 2; ++j) {
|
|
// distance to the center
|
|
distance = sin_quad_texture( (float) c , (float) l );
|
|
// distance = 1.f - (GLfloat) ((c * c) + (l * l)) / CIRCLE_PIXEL_RADIUS; // quadratic
|
|
// distance = 1.f - (GLfloat) sqrt( (GLfloat) ((c * c) + (l * l))) / (GLfloat) sqrt(CIRCLE_PIXEL_RADIUS); // linear
|
|
|
|
// transparency
|
|
alpha = 255.f * CLAMP( distance , 0.f, 1.f);
|
|
color[3] = static_cast<GLubyte>(alpha);
|
|
|
|
// luminance adjustment
|
|
luminance = 255.f * CLAMP( 0.2f + 0.75f * distance, 0.f, 1.f);
|
|
color[0] = color[1] = color[2] = static_cast<GLubyte>(luminance);
|
|
|
|
// 1st quadrant
|
|
memmove(&matrix[ j * 4 + i * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
|
|
// 4nd quadrant
|
|
memmove(&matrix[ (CIRCLE_PIXELS -j -1)* 4 + i * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
|
|
// 3rd quadrant
|
|
memmove(&matrix[ j * 4 + (CIRCLE_PIXELS -i -1) * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
|
|
// 4th quadrant
|
|
memmove(&matrix[ (CIRCLE_PIXELS -j -1) * 4 + (CIRCLE_PIXELS -i -1) * CIRCLE_PIXELS * 4 ], color, 4 * sizeof(GLubyte));
|
|
|
|
++c;
|
|
}
|
|
++l;
|
|
}
|
|
// setup texture
|
|
glGenTextures(1, &texid);
|
|
glBindTexture(GL_TEXTURE_2D, texid);
|
|
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, CIRCLE_PIXELS, CIRCLE_PIXELS);
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, CIRCLE_PIXELS, CIRCLE_PIXELS, GL_BGRA, GL_UNSIGNED_BYTE, matrix);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
|
|
}
|
|
return texid;
|
|
}
|
|
|
|
RenderView::RenderView() : View(RENDERING), frame_buffer_(nullptr), fading_overlay_(nullptr)
|
|
{
|
|
}
|
|
|
|
RenderView::~RenderView()
|
|
{
|
|
if (frame_buffer_)
|
|
delete frame_buffer_;
|
|
if (fading_overlay_)
|
|
delete fading_overlay_;
|
|
}
|
|
|
|
bool RenderView::canSelect(Source *s) {
|
|
|
|
return false;
|
|
}
|
|
|
|
void RenderView::setFading(float f)
|
|
{
|
|
if (fading_overlay_ == nullptr)
|
|
fading_overlay_ = new Surface;
|
|
|
|
fading_overlay_->shader()->color.a = CLAMP( f < EPSILON ? 0.f : f, 0.f, 1.f);
|
|
}
|
|
|
|
float RenderView::fading() const
|
|
{
|
|
if (fading_overlay_)
|
|
return fading_overlay_->shader()->color.a;
|
|
else
|
|
return 0.f;
|
|
}
|
|
|
|
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)
|
|
resolution = FrameBuffer::getResolutionFromParameters(Settings::application.render.ratio, Settings::application.render.res);
|
|
|
|
// do we need to change resolution ?
|
|
if (frame_buffer_ && frame_buffer_->resolution() != resolution) {
|
|
|
|
// new frame buffer
|
|
delete frame_buffer_;
|
|
frame_buffer_ = nullptr;
|
|
}
|
|
|
|
if (!frame_buffer_)
|
|
// output frame is an RBG Multisamples FrameBuffer
|
|
frame_buffer_ = new FrameBuffer(resolution, useAlpha, true);
|
|
|
|
// reset fading
|
|
setFading();
|
|
}
|
|
|
|
void RenderView::draw()
|
|
{
|
|
static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -SCENE_DEPTH, 1.f);
|
|
|
|
if (frame_buffer_) {
|
|
// draw in frame buffer
|
|
glm::mat4 P = glm::scale( projection, glm::vec3(1.f / frame_buffer_->aspectRatio(), 1.f, 1.f));
|
|
frame_buffer_->begin();
|
|
scene.root()->draw(glm::identity<glm::mat4>(), P);
|
|
fading_overlay_->draw(glm::identity<glm::mat4>(), projection);
|
|
frame_buffer_->end();
|
|
}
|
|
}
|
|
|
|
GeometryView::GeometryView() : View(GEOMETRY)
|
|
{
|
|
// read default settings
|
|
if ( Settings::application.views[mode_].name.empty() ) {
|
|
// no settings found: store application default
|
|
Settings::application.views[mode_].name = "Geometry";
|
|
scene.root()->scale_ = glm::vec3(GEOMETRY_DEFAULT_SCALE, GEOMETRY_DEFAULT_SCALE, 1.0f);
|
|
saveSettings();
|
|
}
|
|
else
|
|
restoreSettings();
|
|
|
|
// 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::CROSS);
|
|
overlay_position_cross_->rotation_ = glm::vec3(0.f, 0.f, M_PI_4);
|
|
overlay_position_cross_->scale_ = glm::vec3(0.3f, 0.3f, 1.f);
|
|
scene.fg()->attach(overlay_position_cross_);
|
|
overlay_position_cross_->visible_ = false;
|
|
// 'clock' : tic marks every 10 degrees for ROTATION
|
|
// with dark background
|
|
Group *g = new Group;
|
|
Symbol *s = new Symbol(Symbol::CLOCK);
|
|
g->attach(s);
|
|
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;
|
|
g->attach(s);
|
|
overlay_rotation_clock_ = g;
|
|
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
|
|
g = new Group;
|
|
s = new Symbol(Symbol::GRID);
|
|
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(18.f, 18.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;
|
|
|
|
}
|
|
|
|
void GeometryView::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() );
|
|
}
|
|
}
|
|
}
|
|
|
|
void GeometryView::resize ( int scale )
|
|
{
|
|
float z = CLAMP(0.01f * (float) scale, 0.f, 1.f);
|
|
z *= z;
|
|
z *= GEOMETRY_MAX_SCALE - GEOMETRY_MIN_SCALE;
|
|
z += GEOMETRY_MIN_SCALE;
|
|
scene.root()->scale_.x = z;
|
|
scene.root()->scale_.y = z;
|
|
|
|
// Clamp translation to acceptable area
|
|
glm::vec3 border(scene.root()->scale_.x * 1.5, scene.root()->scale_.y * 1.5, 0.f);
|
|
scene.root()->translation_ = glm::clamp(scene.root()->translation_, -border, border);
|
|
}
|
|
|
|
int GeometryView::size ()
|
|
{
|
|
float z = (scene.root()->scale_.x - GEOMETRY_MIN_SCALE) / (GEOMETRY_MAX_SCALE - GEOMETRY_MIN_SCALE);
|
|
return (int) ( sqrt(z) * 100.f);
|
|
}
|
|
|
|
void showContextMenu(View::Mode m, const char* label)
|
|
{
|
|
if (ImGui::BeginPopup(label)) {
|
|
Source *s = Mixer::manager().currentSource();
|
|
if (s != nullptr) {
|
|
if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE " Reset" )){
|
|
s->group(m)->scale_ = glm::vec3(1.f);
|
|
s->group(m)->rotation_.z = 0;
|
|
s->group(m)->crop_ = glm::vec3(1.f);
|
|
s->group(m)->translation_ = glm::vec3(0.f);
|
|
s->touch();
|
|
}
|
|
else if (ImGui::Selectable( ICON_FA_EXPAND " Fit" )){
|
|
glm::vec3 scale = glm::vec3(1.f);
|
|
if ( m == View::GEOMETRY) {
|
|
FrameBuffer *output = Mixer::manager().session()->frame();
|
|
if (output) scale.x = output->aspectRatio() / s->frame()->aspectRatio();
|
|
}
|
|
else if ( m == View::APPEARANCE ) {
|
|
glm::vec2 crop = s->frame()->projectionArea();
|
|
scale = glm::vec3( crop, 1.f);
|
|
}
|
|
s->group(m)->scale_ = scale;
|
|
s->group(m)->rotation_.z = 0;
|
|
s->group(m)->translation_ = glm::vec3(0.f);
|
|
s->touch();
|
|
}
|
|
else if (ImGui::Selectable( ICON_FA_CROSSHAIRS " Center" )){
|
|
s->group(m)->translation_ = glm::vec3(0.f);
|
|
s->touch();
|
|
}
|
|
else if (ImGui::Selectable( ICON_FA_PERCENTAGE " Original aspect ratio" )){ //ICON_FA_ARROWS_ALT_H
|
|
s->group(m)->scale_.x = s->group(m)->scale_.y;
|
|
s->group(m)->scale_.x *= s->group(m)->crop_.x / s->group(m)->crop_.y;
|
|
s->touch();
|
|
}
|
|
}
|
|
ImGui::EndPopup();
|
|
}
|
|
}
|
|
|
|
void GeometryView::draw()
|
|
{
|
|
// hack to prevent source manipulation (scale and rotate)
|
|
// when multiple sources are selected: simply do not draw overlay in scene
|
|
Source *s = Mixer::manager().currentSource();
|
|
if (s != nullptr) {
|
|
if ( Mixer::selection().size() > 1) {
|
|
s->setMode(Source::SELECTED);
|
|
s = nullptr;
|
|
}
|
|
}
|
|
|
|
// Drawing of Geometry view is different as it renders
|
|
// only sources in the current workspace
|
|
std::vector<Node *> surfaces;
|
|
std::vector<Node *> overlays;
|
|
for (auto source_iter = Mixer::manager().session()->begin();
|
|
source_iter != Mixer::manager().session()->end(); source_iter++) {
|
|
// if it is in the current workspace
|
|
if ((*source_iter)->workspace() == Settings::application.current_workspace) {
|
|
// will draw its surface
|
|
surfaces.push_back((*source_iter)->groups_[mode_]);
|
|
// will draw its frame and locker icon
|
|
overlays.push_back((*source_iter)->frames_[mode_]);
|
|
overlays.push_back((*source_iter)->locker_);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
|
|
// 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 (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("##GeometryViewOptions", 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_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_SliderGrab, ImVec4(0.85f, 0.85f, 0.85f, 0.86f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.95f, 0.95f, 0.95f, 1.00f));
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.14f, 0.14f, 0.14f, 0.46f));
|
|
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.67f, 0.67f, 0.67f, 0.79f));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.44f));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.88f, 0.88f, 0.88f, 0.73f));
|
|
ImGui::PushStyleColor(ImGuiCol_Tab, ImVec4(0.83f, 0.83f, 0.84f, 0.78f));
|
|
ImGui::PushStyleColor(ImGuiCol_TabHovered, ImVec4(0.53f, 0.53f, 0.53f, 0.60f));
|
|
ImGui::PushStyleColor(ImGuiCol_TabActive, ImVec4(0.40f, 0.40f, 0.40f, 1.00f)); // 14 colors
|
|
|
|
static std::vector< std::pair<int, int> > icons_ws = { {10,16}, {11,16}, {12,16} };
|
|
if ( ImGuiToolkit::ComboIcon (icons_ws, &Settings::application.current_workspace) ){
|
|
View::need_deep_update_++;
|
|
}
|
|
|
|
ImGui::PopStyleColor(14); // 14 colors
|
|
ImGui::End();
|
|
}
|
|
ImGui::PopFont();
|
|
|
|
// display popup menu
|
|
if (show_context_menu_) {
|
|
ImGui::OpenPopup( "GeometryContextMenu" );
|
|
show_context_menu_ = false;
|
|
}
|
|
showContextMenu(mode_,"GeometryContextMenu");
|
|
|
|
}
|
|
|
|
|
|
std::pair<Node *, glm::vec2> GeometryView::pick(glm::vec2 P)
|
|
{
|
|
// prepare empty return value
|
|
std::pair<Node *, glm::vec2> 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;
|
|
}
|
|
|
|
// 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();
|
|
}
|
|
// pick on the lock icon; unlock source
|
|
else if ( pick.first == current->lock_ ) {
|
|
current->setLocked(false);
|
|
}
|
|
// pick on the open lock icon; lock source and cancel pick
|
|
else if ( pick.first == current->unlock_ ) {
|
|
current->setLocked(true);
|
|
pick = { nullptr, glm::vec2(0.f) };
|
|
}
|
|
// pick a locked source without CTRL key; cancel pick
|
|
else if ( current->locked() && !UserInterface::manager().ctrlModifier() ) {
|
|
pick = { nullptr, glm::vec2(0.f) };
|
|
}
|
|
}
|
|
// the clicked source changed (not the current source)
|
|
if (current == nullptr){
|
|
|
|
// default to failed pick
|
|
pick = { nullptr, glm::vec2(0.f) };
|
|
|
|
// 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) {
|
|
|
|
// lock icon of a source is picked : unlock
|
|
if ( (*itp).first == s->lock_) {
|
|
s->setLocked(false);
|
|
pick = { s->locker_, (*itp).second };
|
|
break;
|
|
}
|
|
// a non-locked source is picked (or locked with CTRL key)
|
|
else if ( !s->locked() || ( s->locked() && UserInterface::manager().ctrlModifier() ) ) {
|
|
pick = { s->locker_, (*itp).second };
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return pick;
|
|
}
|
|
|
|
bool GeometryView::canSelect(Source *s) {
|
|
|
|
return ( View::canSelect(s) && s->active() && s->workspace() == Settings::application.current_workspace);
|
|
}
|
|
|
|
View::Cursor GeometryView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
|
|
{
|
|
View::Cursor ret = Cursor();
|
|
|
|
// work on the given source
|
|
if (!s)
|
|
return ret;
|
|
|
|
Group *sourceNode = s->group(mode_); // groups_[View::GEOMETRY]
|
|
|
|
// 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;
|
|
|
|
// 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;
|
|
if (pick.first) {
|
|
// which corner was picked ?
|
|
glm::vec2 corner = glm::round(pick.second);
|
|
|
|
// transform from source 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 / s->frame()->aspectRatio(), 1.f, 1.f));
|
|
|
|
// transformation from scene to corner:
|
|
glm::mat4 scene_to_corner_transform = T * glm::inverse(s->stored_status_->transform_);
|
|
glm::mat4 corner_to_scene_transform = glm::inverse(scene_to_corner_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);
|
|
|
|
// convert source position in corner reference frame
|
|
glm::vec4 center = scene_to_corner_transform * glm::vec4( s->stored_status_->translation_, 1.f);
|
|
|
|
// picking on the resizing handles in the corners
|
|
if ( pick.first == s->handles_[mode_][Handles::RESIZE] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::CROP]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
// inform on which corner should be overlayed (opposite)
|
|
s->handles_[mode_][Handles::RESIZE]->overlayActiveCorner(-corner);
|
|
// RESIZE CORNER
|
|
// 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
|
|
sourceNode->scale_ = s->stored_status_->scale_ * glm::vec3(factor, factor, 1.f);
|
|
// discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.x = ROUND(sourceNode->scale_.x, 10.f);
|
|
factor = sourceNode->scale_.x / s->stored_status_->scale_.x;
|
|
sourceNode->scale_.y = s->stored_status_->scale_.y * factor;
|
|
}
|
|
// update corner scaling to apply to center coordinates
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
// non-proportional CORNER RESIZE (normal case)
|
|
else {
|
|
// scale node
|
|
sourceNode->scale_ = s->stored_status_->scale_ * corner_scaling;
|
|
// discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.x = ROUND(sourceNode->scale_.x, 10.f);
|
|
sourceNode->scale_.y = ROUND(sourceNode->scale_.y, 10.f);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
}
|
|
// 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_scene_transform * center;
|
|
// apply to node
|
|
sourceNode->translation_ = glm::vec3(center);
|
|
// show cursor depending on diagonal (corner picked)
|
|
T = glm::rotate(glm::identity<glm::mat4>(), s->stored_status_->rotation_.z, glm::vec3(0.f, 0.f, 1.f));
|
|
T = glm::scale(T, s->stored_status_->scale_);
|
|
corner = T * glm::vec4( corner, 0.f, 0.f );
|
|
ret.type = corner.x * corner.y > 0.f ? Cursor_ResizeNESW : Cursor_ResizeNWSE;
|
|
info << "Size " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y;
|
|
|
|
}
|
|
// picking on the BORDER RESIZING handles left or right
|
|
else if ( pick.first == s->handles_[mode_][Handles::RESIZE_H] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::CROP]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
// inform on which corner should be overlayed (opposite)
|
|
s->handles_[mode_][Handles::RESIZE_H]->overlayActiveCorner(-corner);
|
|
// SHIFT: HORIZONTAL SCALE to restore source aspect ratio
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
sourceNode->scale_.x = ABS(sourceNode->scale_.y) * SIGN(sourceNode->scale_.x);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
// HORIZONTAL RESIZE (normal case)
|
|
else {
|
|
// x scale only
|
|
corner_scaling = glm::vec3(corner_scaling.x, 1.f, 1.f);
|
|
// scale node
|
|
sourceNode->scale_ = s->stored_status_->scale_ * corner_scaling;
|
|
// POST-CORRECTION ; discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.x = ROUND(sourceNode->scale_.x, 10.f);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
}
|
|
// 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_scene_transform * center;
|
|
// apply to node
|
|
sourceNode->translation_ = glm::vec3(center);
|
|
// show cursor depending on angle
|
|
float c = tan(sourceNode->rotation_.z);
|
|
ret.type = ABS(c) > 1.f ? Cursor_ResizeNS : Cursor_ResizeEW;
|
|
info << "Size " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y;
|
|
}
|
|
// picking on the BORDER RESIZING handles top or bottom
|
|
else if ( pick.first == s->handles_[mode_][Handles::RESIZE_V] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::CROP]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
// inform on which corner should be overlayed (opposite)
|
|
s->handles_[mode_][Handles::RESIZE_V]->overlayActiveCorner(-corner);
|
|
// SHIFT: VERTICAL SCALE to restore source aspect ratio
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
sourceNode->scale_.y = ABS(sourceNode->scale_.x) * SIGN(sourceNode->scale_.y);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
// VERTICAL RESIZE (normal case)
|
|
else {
|
|
// y scale only
|
|
corner_scaling = glm::vec3(1.f, corner_scaling.y, 1.f);
|
|
// scale node
|
|
sourceNode->scale_ = s->stored_status_->scale_ * corner_scaling;
|
|
// POST-CORRECTION ; discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.y = ROUND(sourceNode->scale_.y, 10.f);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
}
|
|
// 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_scene_transform * center;
|
|
// apply to node
|
|
sourceNode->translation_ = glm::vec3(center);
|
|
// show cursor depending on angle
|
|
float c = tan(sourceNode->rotation_.z);
|
|
ret.type = ABS(c) > 1.f ? Cursor_ResizeEW : Cursor_ResizeNS;
|
|
info << "Size " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y;
|
|
}
|
|
// picking on the CENTRER SCALING handle
|
|
else if ( pick.first == s->handles_[mode_][Handles::SCALE] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::CROP]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
// prepare overlay
|
|
overlay_scaling_cross_->visible_ = false;
|
|
overlay_scaling_grid_->visible_ = false;
|
|
overlay_scaling_->visible_ = true;
|
|
overlay_scaling_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_scaling_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_scaling_->rotation_.z = s->stored_status_->rotation_.z;
|
|
overlay_scaling_->update(0);
|
|
// PROPORTIONAL ONLY
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
float factor = glm::length( glm::vec2( source_to ) ) / glm::length( glm::vec2( source_from ) );
|
|
source_scaling = glm::vec3(factor, factor, 1.f);
|
|
overlay_scaling_cross_->visible_ = true;
|
|
overlay_scaling_cross_->copyTransform(overlay_scaling_);
|
|
}
|
|
// apply center scaling
|
|
sourceNode->scale_ = s->stored_status_->scale_ * source_scaling;
|
|
// POST-CORRECTION ; discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.x = ROUND(sourceNode->scale_.x, 10.f);
|
|
sourceNode->scale_.y = ROUND(sourceNode->scale_.y, 10.f);
|
|
overlay_scaling_grid_->visible_ = true;
|
|
overlay_scaling_grid_->copyTransform(overlay_scaling_);
|
|
}
|
|
// show cursor depending on diagonal
|
|
corner = glm::sign(sourceNode->scale_);
|
|
ret.type = (corner.x * corner.y) > 0.f ? Cursor_ResizeNWSE : Cursor_ResizeNESW;
|
|
info << "Size " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y;
|
|
}
|
|
// picking on the CROP
|
|
else if ( pick.first == s->handles_[mode_][Handles::CROP] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
|
|
// prepare overlay
|
|
overlay_crop_->scale_ = s->stored_status_->scale_ / s->stored_status_->crop_;
|
|
overlay_crop_->scale_.x *= s->frame()->aspectRatio();
|
|
overlay_crop_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_crop_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_crop_->rotation_.z = s->stored_status_->rotation_.z;
|
|
overlay_crop_->update(0);
|
|
overlay_crop_->visible_ = true;
|
|
|
|
// PROPORTIONAL ONLY
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
float factor = glm::length( glm::vec2( source_to ) ) / glm::length( glm::vec2( source_from ) );
|
|
source_scaling = glm::vec3(factor, factor, 1.f);
|
|
}
|
|
// calculate crop of framebuffer
|
|
sourceNode->crop_ = s->stored_status_->crop_ * source_scaling;
|
|
// POST-CORRECTION ; discretized crop with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->crop_.x = ROUND(sourceNode->crop_.x, 10.f);
|
|
sourceNode->crop_.y = ROUND(sourceNode->crop_.y, 10.f);
|
|
}
|
|
// CLAMP crop values
|
|
sourceNode->crop_.x = CLAMP(sourceNode->crop_.x, 0.1f, 1.f);
|
|
sourceNode->crop_.y = CLAMP(sourceNode->crop_.y, 0.1f, 1.f);
|
|
// apply center scaling
|
|
s->frame()->setProjectionArea( glm::vec2(sourceNode->crop_) );
|
|
sourceNode->scale_ = s->stored_status_->scale_ * (sourceNode->crop_ / s->stored_status_->crop_);
|
|
// show cursor depending on diagonal
|
|
corner = glm::sign(sourceNode->scale_);
|
|
ret.type = (corner.x * corner.y) < 0.f ? Cursor_ResizeNWSE : Cursor_ResizeNESW;
|
|
info << "Crop " << std::fixed << std::setprecision(3) << sourceNode->crop_.x;
|
|
info << " x " << sourceNode->crop_.y;
|
|
}
|
|
// picking on the rotating handle
|
|
else if ( pick.first == s->handles_[mode_][Handles::ROTATE] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::CROP]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
|
|
// ROTATION on CENTER
|
|
overlay_rotation_->visible_ = true;
|
|
overlay_rotation_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_rotation_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_rotation_->update(0);
|
|
overlay_rotation_fix_->visible_ = true;
|
|
overlay_rotation_fix_->copyTransform(overlay_rotation_);
|
|
overlay_rotation_clock_->visible_ = false;
|
|
|
|
// rotation center to center of source (disregarding scale)
|
|
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), s->stored_status_->translation_);
|
|
source_from = glm::inverse(T) * glm::vec4( scene_from, 1.f );
|
|
source_to = glm::inverse(T) * glm::vec4( scene_to, 1.f );
|
|
// compute rotation angle
|
|
float angle = glm::orientedAngle( glm::normalize(glm::vec2(source_from)), glm::normalize(glm::vec2(source_to)));
|
|
// apply rotation on Z axis
|
|
sourceNode->rotation_ = s->stored_status_->rotation_ + glm::vec3(0.f, 0.f, angle);
|
|
|
|
// POST-CORRECTION ; discretized rotation with ALT
|
|
int degrees = int( glm::degrees(sourceNode->rotation_.z) );
|
|
if (UserInterface::manager().altModifier()) {
|
|
degrees = (degrees / 10) * 10;
|
|
sourceNode->rotation_.z = glm::radians( float(degrees) );
|
|
overlay_rotation_clock_->visible_ = true;
|
|
overlay_rotation_clock_->copyTransform(overlay_rotation_);
|
|
info << "Angle " << degrees << "\u00b0"; // degree symbol
|
|
}
|
|
else
|
|
info << "Angle " << std::fixed << std::setprecision(1) << glm::degrees(sourceNode->rotation_.z) << "\u00b0"; // degree symbol
|
|
|
|
overlay_rotation_clock_hand_->visible_ = true;
|
|
overlay_rotation_clock_hand_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_rotation_clock_hand_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_rotation_clock_hand_->rotation_.z = sourceNode->rotation_.z;
|
|
overlay_rotation_clock_hand_->update(0);
|
|
|
|
// show cursor for rotation
|
|
ret.type = Cursor_Hand;
|
|
// + SHIFT = no scaling / NORMAL = with scaling
|
|
if (!UserInterface::manager().shiftModifier()) {
|
|
// compute scaling to match cursor
|
|
float factor = glm::length( glm::vec2( source_to ) ) / glm::length( glm::vec2( source_from ) );
|
|
source_scaling = glm::vec3(factor, factor, 1.f);
|
|
// apply center scaling
|
|
sourceNode->scale_ = s->stored_status_->scale_ * source_scaling;
|
|
info << std::endl << " Size " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y ;
|
|
overlay_rotation_fix_->visible_ = false;
|
|
}
|
|
}
|
|
// picking anywhere but on a handle: user wants to move the source
|
|
else {
|
|
ret.type = Cursor_ResizeAll;
|
|
sourceNode->translation_ = s->stored_status_->translation_ + scene_translation;
|
|
// discretized translation with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->translation_.x = ROUND(sourceNode->translation_.x, 10.f);
|
|
sourceNode->translation_.y = ROUND(sourceNode->translation_.y, 10.f);
|
|
}
|
|
// ALT: single axis movement
|
|
overlay_position_cross_->visible_ = false;
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
overlay_position_cross_->visible_ = true;
|
|
overlay_position_cross_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_position_cross_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_position_cross_->update(0);
|
|
|
|
glm::vec3 dif = s->stored_status_->translation_ - sourceNode->translation_;
|
|
if (ABS(dif.x) > ABS(dif.y) ) {
|
|
sourceNode->translation_.y = s->stored_status_->translation_.y;
|
|
ret.type = Cursor_ResizeEW;
|
|
} else {
|
|
sourceNode->translation_.x = s->stored_status_->translation_.x;
|
|
ret.type = Cursor_ResizeNS;
|
|
}
|
|
}
|
|
// Show center overlay for POSITION
|
|
overlay_position_->visible_ = true;
|
|
overlay_position_->translation_.x = sourceNode->translation_.x;
|
|
overlay_position_->translation_.y = sourceNode->translation_.y;
|
|
overlay_position_->update(0);
|
|
// Show move cursor
|
|
info << "Position " << std::fixed << std::setprecision(3) << sourceNode->translation_.x;
|
|
info << ", " << sourceNode->translation_.y ;
|
|
}
|
|
}
|
|
|
|
// request update
|
|
s->touch();
|
|
|
|
// store action in history
|
|
current_action_ = s->name() + ": " + info.str();
|
|
current_id_ = s->id();
|
|
|
|
// update cursor
|
|
ret.info = info.str();
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
void GeometryView::terminate()
|
|
{
|
|
View::terminate();
|
|
|
|
// 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 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;
|
|
}
|
|
|
|
}
|
|
|
|
void GeometryView::arrow (glm::vec2 movement)
|
|
{
|
|
Source *s = Mixer::manager().currentSource();
|
|
if (s) {
|
|
|
|
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;
|
|
|
|
Group *sourceNode = s->group(mode_);
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->translation_ += glm::vec3(movement.x, -movement.y, 0.f) * 0.1f;
|
|
sourceNode->translation_.x = ROUND(sourceNode->translation_.x, 10.f);
|
|
sourceNode->translation_.y = ROUND(sourceNode->translation_.y, 10.f);
|
|
}
|
|
else
|
|
sourceNode->translation_ += gl_delta * ARROWS_MOVEMENT_FACTOR;
|
|
|
|
// request update
|
|
s->touch();
|
|
}
|
|
}
|
|
|
|
LayerView::LayerView() : View(LAYER), aspect_ratio(1.f)
|
|
{
|
|
// read default settings
|
|
if ( Settings::application.views[mode_].name.empty() ) {
|
|
// no settings found: store application default
|
|
Settings::application.views[mode_].name = "Layer";
|
|
scene.root()->scale_ = glm::vec3(LAYER_DEFAULT_SCALE, LAYER_DEFAULT_SCALE, 1.0f);
|
|
scene.root()->translation_ = glm::vec3(2.2f, 1.2f, 0.0f);
|
|
saveSettings();
|
|
}
|
|
else
|
|
restoreSettings();
|
|
|
|
// Geometry Scene background
|
|
frame_ = new Group;
|
|
Surface *rect = new Surface;
|
|
rect->shader()->color.a = 0.3f;
|
|
frame_->attach(rect);
|
|
|
|
Frame *border = new Frame(Frame::ROUND, Frame::THIN, Frame::PERSPECTIVE);
|
|
border->color = glm::vec4( COLOR_FRAME, 0.95f );
|
|
frame_->attach(border);
|
|
scene.bg()->attach(frame_);
|
|
|
|
persp_left_ = new Mesh("mesh/perspective_axis_left.ply");
|
|
persp_left_->shader()->color = glm::vec4( COLOR_FRAME_LIGHT, 1.f );
|
|
persp_left_->scale_.x = LAYER_PERSPECTIVE;
|
|
persp_left_->translation_.z = -0.1f;
|
|
scene.bg()->attach(persp_left_);
|
|
|
|
persp_right_ = new Mesh("mesh/perspective_axis_right.ply");
|
|
persp_right_->shader()->color = glm::vec4( COLOR_FRAME_LIGHT, 1.f );
|
|
persp_right_->scale_.x = LAYER_PERSPECTIVE;
|
|
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::LARGE, 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);
|
|
|
|
// 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){
|
|
// correct with aspect ratio
|
|
aspect_ratio = output->aspectRatio();
|
|
frame_->scale_.x = aspect_ratio;
|
|
persp_left_->translation_.x = -aspect_ratio;
|
|
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) {
|
|
|
|
return ( View::canSelect(s) && s->active() );
|
|
}
|
|
|
|
void LayerView::resize ( int scale )
|
|
{
|
|
float z = CLAMP(0.01f * (float) scale, 0.f, 1.f);
|
|
z *= z;
|
|
z *= LAYER_MAX_SCALE - LAYER_MIN_SCALE;
|
|
z += LAYER_MIN_SCALE;
|
|
scene.root()->scale_.x = z;
|
|
scene.root()->scale_.y = z;
|
|
|
|
// Clamp translation to acceptable area
|
|
glm::vec3 border_left(scene.root()->scale_.x * -2.f, scene.root()->scale_.y * -1.f, 0.f);
|
|
glm::vec3 border_right(scene.root()->scale_.x * 8.f, scene.root()->scale_.y * 8.f, 0.f);
|
|
scene.root()->translation_ = glm::clamp(scene.root()->translation_, border_left, border_right);
|
|
}
|
|
|
|
int LayerView::size ()
|
|
{
|
|
float z = (scene.root()->scale_.x - LAYER_MIN_SCALE) / (LAYER_MAX_SCALE - LAYER_MIN_SCALE);
|
|
return (int) ( sqrt(z) * 100.f);
|
|
}
|
|
|
|
|
|
std::pair<Node *, glm::vec2> LayerView::pick(glm::vec2 P)
|
|
{
|
|
// get picking from generic View
|
|
std::pair<Node *, glm::vec2> pick = View::pick(P);
|
|
|
|
// deal with internal interactive objects
|
|
if ( pick.first == overlay_group_icon_ ) {
|
|
|
|
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) };
|
|
}
|
|
}
|
|
|
|
return pick;
|
|
}
|
|
|
|
|
|
float LayerView::setDepth(Source *s, float d)
|
|
{
|
|
if (!s)
|
|
return -1.f;
|
|
|
|
// move the layer node of the source
|
|
Group *sourceNode = s->group(mode_);
|
|
|
|
float depth = d < 0.f ? sourceNode->translation_.z : d;
|
|
|
|
// negative or no depth given; find the front most depth
|
|
if ( depth < 0.f ) {
|
|
// default to place visible in front of background
|
|
depth = LAYER_BACKGROUND + LAYER_STEP;
|
|
|
|
// find the front-most souce in the workspace (behind FOREGROUND)
|
|
for (NodeSet::iterator node = scene.ws()->begin(); node != scene.ws()->end(); node++) {
|
|
// place in front of previous sources
|
|
depth = MAX(depth, (*node)->translation_.z + LAYER_STEP);
|
|
|
|
// in case node is already at max depth
|
|
if ((*node)->translation_.z + DELTA_DEPTH > MAX_DEPTH )
|
|
(*node)->translation_.z -= DELTA_DEPTH;
|
|
}
|
|
}
|
|
|
|
// change depth
|
|
sourceNode->translation_.z = CLAMP( depth, MIN_DEPTH, MAX_DEPTH);
|
|
|
|
// discretized translation with ALT
|
|
if (UserInterface::manager().altModifier())
|
|
sourceNode->translation_.z = ROUND(sourceNode->translation_.z, 5.f);
|
|
|
|
// request reordering of scene at next update
|
|
View::need_deep_update_++;
|
|
|
|
// request update of source
|
|
s->touch();
|
|
|
|
return sourceNode->translation_.z;
|
|
}
|
|
|
|
|
|
View::Cursor LayerView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
|
|
{
|
|
if (!s)
|
|
return Cursor();
|
|
|
|
// unproject
|
|
glm::vec3 gl_Position_from = Rendering::manager().unProject(from, scene.root()->transform_);
|
|
glm::vec3 gl_Position_to = Rendering::manager().unProject(to, scene.root()->transform_);
|
|
|
|
// compute delta translation
|
|
glm::vec3 dest_translation = s->stored_status_->translation_ + gl_Position_to - gl_Position_from;
|
|
|
|
// apply change
|
|
float d = setDepth( s, MAX( -dest_translation.x, 0.f) );
|
|
|
|
std::ostringstream info;
|
|
info << "Depth " << std::fixed << std::setprecision(2) << d << " ";
|
|
// info << (s->locked() ? ICON_FA_LOCK : ICON_FA_LOCK_OPEN); // TODO static not locked
|
|
|
|
// store action in history
|
|
current_action_ = s->name() + ": " + info.str();
|
|
current_id_ = s->id();
|
|
|
|
return Cursor(Cursor_ResizeNESW, info.str() );
|
|
}
|
|
|
|
void LayerView::arrow (glm::vec2 movement)
|
|
{
|
|
Source *s = Mixer::manager().currentSource();
|
|
if (s) {
|
|
|
|
glm::vec3 gl_Position_from = Rendering::manager().unProject(glm::vec2(0.f), scene.root()->transform_);
|
|
glm::vec3 gl_Position_to = Rendering::manager().unProject(glm::vec2(movement.x-movement.y, 0.f), scene.root()->transform_);
|
|
glm::vec3 gl_delta = gl_Position_to - gl_Position_from;
|
|
if (UserInterface::manager().altModifier())
|
|
gl_delta *= 10.f;
|
|
|
|
Group *sourceNode = s->group(mode_);
|
|
glm::vec3 dest_translation = sourceNode->translation_ + gl_delta * ARROWS_MOVEMENT_FACTOR;
|
|
setDepth( s, MAX( -dest_translation.x, 0.f) );
|
|
|
|
// request update
|
|
s->touch();
|
|
}
|
|
}
|
|
|
|
|
|
// TRANSITION
|
|
|
|
TransitionView::TransitionView() : View(TRANSITION), transition_source_(nullptr)
|
|
{
|
|
// read default settings
|
|
if ( Settings::application.views[mode_].name.empty() )
|
|
{
|
|
// no settings found: store application default
|
|
Settings::application.views[mode_].name = "Transition";
|
|
scene.root()->scale_ = glm::vec3(TRANSITION_DEFAULT_SCALE, TRANSITION_DEFAULT_SCALE, 1.0f);
|
|
scene.root()->translation_ = glm::vec3(1.5f, 0.f, 0.0f);
|
|
saveSettings();
|
|
}
|
|
else
|
|
restoreSettings();
|
|
|
|
// Geometry Scene background
|
|
gradient_ = new Switch;
|
|
gradient_->attach(new ImageSurface("images/gradient_0_cross_linear.png"));
|
|
gradient_->attach(new ImageSurface("images/gradient_1_black_linear.png"));
|
|
gradient_->attach(new ImageSurface("images/gradient_2_cross_quad.png"));
|
|
gradient_->attach(new ImageSurface("images/gradient_3_black_quad.png"));
|
|
gradient_->scale_ = glm::vec3(0.501f, 0.006f, 1.f);
|
|
gradient_->translation_ = glm::vec3(-0.5f, -0.005f, -0.01f);
|
|
scene.fg()->attach(gradient_);
|
|
|
|
// Mesh *horizontal_line = new Mesh("mesh/h_line.ply");
|
|
// horizontal_line->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f );
|
|
// scene.fg()->attach(horizontal_line);
|
|
mark_1s_ = new Mesh("mesh/h_mark.ply");
|
|
mark_1s_->translation_ = glm::vec3(-1.f, -0.01f, 0.0f);
|
|
mark_1s_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f );
|
|
scene.fg()->attach(mark_1s_);
|
|
|
|
mark_100ms_ = new Mesh("mesh/h_mark.ply");
|
|
mark_100ms_->translation_ = glm::vec3(-1.f, -0.01f, 0.0f);
|
|
mark_100ms_->scale_ = glm::vec3(0.5f, 0.5f, 0.0f);
|
|
mark_100ms_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f );
|
|
scene.fg()->attach(mark_100ms_);
|
|
|
|
// move the whole forground below the icons
|
|
scene.fg()->translation_ = glm::vec3(0.f, -0.11f, 0.0f);
|
|
|
|
output_surface_ = new Surface;
|
|
scene.bg()->attach(output_surface_);
|
|
|
|
Frame *border = new Frame(Frame::ROUND, Frame::THIN, Frame::GLOW);
|
|
border->color = glm::vec4( COLOR_FRAME, 1.0f );
|
|
scene.bg()->attach(border);
|
|
|
|
scene.bg()->scale_ = glm::vec3(0.1f, 0.1f, 1.f);
|
|
scene.bg()->translation_ = glm::vec3(0.4f, 0.f, 0.0f);
|
|
|
|
}
|
|
|
|
void TransitionView::update(float dt)
|
|
{
|
|
// update scene
|
|
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;
|
|
}
|
|
output_surface_->setTextureIndex( output->texture() );
|
|
}
|
|
|
|
}
|
|
|
|
// Update transition source
|
|
if ( transition_source_ != nullptr) {
|
|
|
|
float d = transition_source_->group(View::TRANSITION)->translation_.x;
|
|
|
|
// Transfer this movement to changes in mixing
|
|
// cross fading
|
|
if ( Settings::application.transition.cross_fade )
|
|
{
|
|
float f = 0.f;
|
|
// change alpha of session:
|
|
if (Settings::application.transition.profile == 0)
|
|
// linear => identical coordinates in Mixing View
|
|
f = d;
|
|
else {
|
|
// quadratic => square coordinates in Mixing View
|
|
f = (d+1.f)*(d+1.f) -1.f;
|
|
}
|
|
transition_source_->group(View::MIXING)->translation_.x = CLAMP(f, -1.f, 0.f);
|
|
transition_source_->group(View::MIXING)->translation_.y = 0.f;
|
|
|
|
}
|
|
// fade to black
|
|
else
|
|
{
|
|
// change alpha of session ; hidden before -0.5, visible after
|
|
transition_source_->group(View::MIXING)->translation_.x = d < -0.5f ? -1.f : 0.f;
|
|
transition_source_->group(View::MIXING)->translation_.y = 0.f;
|
|
|
|
// fade to black at 50% : fade-out [-1.0 -0.5], fade-in [-0.5 0.0]
|
|
float f = 0.f;
|
|
if (Settings::application.transition.profile == 0)
|
|
f = ABS(2.f * d + 1.f); // linear
|
|
else {
|
|
f = ( 2.f * d + 1.f); // quadratic
|
|
f *= f;
|
|
}
|
|
Mixer::manager().session()->setFading( 1.f - f );
|
|
}
|
|
|
|
// request update
|
|
transition_source_->touch();
|
|
|
|
if (d > 0.2f && Settings::application.transition.auto_open)
|
|
Mixer::manager().setView(View::MIXING);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
void TransitionView::draw()
|
|
{
|
|
// update the GUI depending on changes in settings
|
|
gradient_->setActive( 2*Settings::application.transition.profile + (Settings::application.transition.cross_fade ? 0 : 1) );
|
|
|
|
// draw scene of this view
|
|
View::draw();
|
|
|
|
// 100ms tic marks
|
|
int n = static_cast<int>( Settings::application.transition.duration / 0.1f );
|
|
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), glm::vec3( 1.f / n, 0.f, 0.f));
|
|
DrawVisitor dv(mark_100ms_, Rendering::manager().Projection());
|
|
dv.loop(n+1, T);
|
|
scene.accept(dv);
|
|
|
|
// 1s tic marks
|
|
int N = static_cast<int>( Settings::application.transition.duration );
|
|
T = glm::translate(glm::identity<glm::mat4>(), glm::vec3( 10.f / n, 0.f, 0.f));
|
|
DrawVisitor dv2(mark_1s_, Rendering::manager().Projection());
|
|
dv2.loop(N+1, T);
|
|
scene.accept(dv2);
|
|
|
|
// display interface duration
|
|
glm::vec2 P = Rendering::manager().project(glm::vec3(-0.17f, -0.14f, 0.f), scene.root()->transform_, false);
|
|
ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always);
|
|
if (ImGui::Begin("##Transition", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground
|
|
| ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings
|
|
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus))
|
|
{
|
|
|
|
// style grey
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.27f, 0.27f, 0.27f, 0.55f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.27f, 0.27f, 0.27f, 0.79f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.27f, 0.27f, 0.27f, 0.7f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.15f, 0.15f, 0.15f, 1.00f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.10f, 0.10f, 0.10f, 1.00f));
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.27f, 0.27f, 0.27f, 0.55f)); // 7 colors
|
|
|
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
|
|
ImGui::SetNextItemWidth(180.f);
|
|
ImGui::SliderFloat("##transitionduration", &Settings::application.transition.duration,
|
|
TRANSITION_MIN_DURATION, TRANSITION_MAX_DURATION, "%.1f s");
|
|
ImGui::SameLine();
|
|
if ( ImGui::Button(ICON_FA_STEP_FORWARD) )
|
|
play(false);
|
|
|
|
ImGui::PopFont();
|
|
ImGui::PopStyleColor(7); // 7 colors
|
|
ImGui::End();
|
|
}
|
|
|
|
P = Rendering::manager().project(glm::vec3(-0.535f, -0.14f, 0.f), scene.root()->transform_, false);
|
|
ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always);
|
|
if (ImGui::Begin("##TransitionType", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground
|
|
| ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings
|
|
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus))
|
|
{
|
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
|
|
|
|
// black background in icon 'transition to black'
|
|
if (!Settings::application.transition.cross_fade) {
|
|
ImVec2 draw_pos = ImGui::GetCursorScreenPos();
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.0f, 0.0f, 0.0f, 1.f));
|
|
ImGuiToolkit::Icon(19,1);
|
|
ImGui::PopStyleColor();
|
|
ImGui::SetCursorScreenPos(draw_pos);
|
|
}
|
|
|
|
// toggle transition mode
|
|
const char *tooltip[2] = {"Transition to black", "Cross fading"};
|
|
ImGuiToolkit::IconToggle(0,2,0,8, &Settings::application.transition.cross_fade, tooltip );
|
|
|
|
ImGui::PopFont();
|
|
ImGui::End();
|
|
}
|
|
|
|
}
|
|
|
|
bool TransitionView::canSelect(Source *s) {
|
|
|
|
return ( s!=nullptr && s == transition_source_);
|
|
}
|
|
|
|
void TransitionView::attach(SessionFileSource *ts)
|
|
{
|
|
// store source for later (detatch & interaction)
|
|
transition_source_ = ts;
|
|
|
|
if ( transition_source_ != nullptr) {
|
|
// insert in scene
|
|
Group *tg = transition_source_->group(View::TRANSITION);
|
|
tg->visible_ = true;
|
|
scene.ws()->attach(tg);
|
|
|
|
// in fade to black transition, start transition from current fading value
|
|
if ( !Settings::application.transition.cross_fade) {
|
|
|
|
// reverse calculate x position to match actual fading of session
|
|
float d = 0.f;
|
|
if (Settings::application.transition.profile == 0)
|
|
d = -1.f + 0.5f * Mixer::manager().session()->fading(); // linear
|
|
else {
|
|
d = -1.f - 0.5f * ( sqrt(1.f - Mixer::manager().session()->fading()) - 1.f); // quadratic
|
|
}
|
|
|
|
transition_source_->group(View::TRANSITION)->translation_.x = d;
|
|
}
|
|
}
|
|
}
|
|
|
|
Session *TransitionView::detach()
|
|
{
|
|
// by default, nothing to return
|
|
Session *ret = nullptr;
|
|
|
|
if ( transition_source_ != nullptr) {
|
|
|
|
// get and detatch the group node from the view workspace
|
|
Group *tg = transition_source_->group(View::TRANSITION);
|
|
scene.ws()->detach( tg );
|
|
|
|
|
|
// test if the icon of the transition source is "Ready"
|
|
if ( tg->translation_.x > 0.f )
|
|
// detatch the session and return it
|
|
ret = transition_source_->detach();
|
|
else
|
|
// not detached: make current
|
|
Mixer::manager().setCurrentSource(transition_source_);
|
|
|
|
// done with transition
|
|
transition_source_ = nullptr;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void TransitionView::zoom (float factor)
|
|
{
|
|
if (transition_source_ != nullptr) {
|
|
float d = transition_source_->group(View::TRANSITION)->translation_.x;
|
|
d += 0.1f * factor;
|
|
transition_source_->group(View::TRANSITION)->translation_.x = CLAMP(d, -1.f, 0.f);
|
|
}
|
|
}
|
|
|
|
std::pair<Node *, glm::vec2> TransitionView::pick(glm::vec2 P)
|
|
{
|
|
std::pair<Node *, glm::vec2> pick = View::pick(P);
|
|
|
|
if (transition_source_ != nullptr) {
|
|
// start animation when clic on target
|
|
if (pick.first == output_surface_)
|
|
play(true);
|
|
// otherwise cancel animation
|
|
else
|
|
transition_source_->group(View::TRANSITION)->clearCallbacks();
|
|
}
|
|
|
|
return pick;
|
|
}
|
|
|
|
|
|
void TransitionView::play(bool open)
|
|
{
|
|
if (transition_source_ != nullptr) {
|
|
|
|
// toggle play/stop with button
|
|
if (!transition_source_->group(View::TRANSITION)->update_callbacks_.empty() && !open) {
|
|
// just cancel previous animation
|
|
transition_source_->group(View::TRANSITION)->clearCallbacks();
|
|
return;
|
|
}
|
|
// else cancel previous animation and start new one
|
|
transition_source_->group(View::TRANSITION)->clearCallbacks();
|
|
|
|
// if want to open session after play, target movement till end position, otherwise stop at 0
|
|
float target_x = open ? 0.4f : 0.f;
|
|
|
|
// calculate how far to reach target
|
|
float time = CLAMP(- transition_source_->group(View::TRANSITION)->translation_.x, 0.f, 1.f);
|
|
// extra distance to reach transition if want to open
|
|
time += open ? 0.2f : 0.f;
|
|
// calculate remaining time on the total duration, in ms
|
|
time *= Settings::application.transition.duration * 1000.f;
|
|
|
|
|
|
// if remaining time is more than 50ms
|
|
if (time > 50.f) {
|
|
// start animation
|
|
MoveToCallback *anim = new MoveToCallback(glm::vec3(target_x, 0.0, 0.0), time);
|
|
transition_source_->group(View::TRANSITION)->update_callbacks_.push_back(anim);
|
|
}
|
|
// otherwise finish animation
|
|
else
|
|
transition_source_->group(View::TRANSITION)->translation_.x = target_x;
|
|
}
|
|
}
|
|
|
|
View::Cursor TransitionView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2>)
|
|
{
|
|
if (!s)
|
|
return Cursor();
|
|
|
|
// unproject
|
|
glm::vec3 gl_Position_from = Rendering::manager().unProject(from, scene.root()->transform_);
|
|
glm::vec3 gl_Position_to = Rendering::manager().unProject(to, scene.root()->transform_);
|
|
|
|
// compute delta translation
|
|
float d = s->stored_status_->translation_.x + gl_Position_to.x - gl_Position_from.x;
|
|
std::ostringstream info;
|
|
if (d > 0.2) {
|
|
s->group(mode_)->translation_.x = 0.4;
|
|
info << "Open session";
|
|
}
|
|
else {
|
|
s->group(mode_)->translation_.x = CLAMP(d, -1.f, 0.f);
|
|
info << "Transition " << int( 100.f * (1.f + s->group(View::TRANSITION)->translation_.x)) << "%";
|
|
}
|
|
|
|
return Cursor(Cursor_ResizeEW, info.str() );
|
|
}
|
|
|
|
void TransitionView::arrow (glm::vec2 movement)
|
|
{
|
|
Source *s = Mixer::manager().currentSource();
|
|
if (s) {
|
|
|
|
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;
|
|
|
|
float d = s->group(mode_)->translation_.x + gl_delta.x * ARROWS_MOVEMENT_FACTOR;
|
|
s->group(mode_)->translation_.x = CLAMP(d, -1.f, 0.f);
|
|
|
|
// request update
|
|
s->touch();
|
|
}
|
|
}
|
|
|
|
View::Cursor TransitionView::drag (glm::vec2 from, glm::vec2 to)
|
|
{
|
|
Cursor ret = View::drag(from, to);
|
|
|
|
// Clamp translation to acceptable area
|
|
scene.root()->translation_ = glm::clamp(scene.root()->translation_, glm::vec3(1.f, -1.7f, 0.f), glm::vec3(2.f, 1.7f, 0.f));
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
AppearanceView::AppearanceView() : View(APPEARANCE), edit_source_(nullptr), need_edit_update_(true)
|
|
{
|
|
// read default settings
|
|
if ( Settings::application.views[mode_].name.empty() ) {
|
|
// no settings found: store application default
|
|
Settings::application.views[mode_].name = "Appearance";
|
|
scene.root()->scale_ = glm::vec3(APPEARANCE_DEFAULT_SCALE, APPEARANCE_DEFAULT_SCALE, 1.0f);
|
|
scene.root()->translation_ = glm::vec3(0.8f, 0.f, 0.0f);
|
|
saveSettings();
|
|
}
|
|
else
|
|
restoreSettings();
|
|
|
|
//
|
|
// Scene background
|
|
//
|
|
// global dark
|
|
Surface *tmp = new Surface( new Shader);
|
|
tmp->scale_ = glm::vec3(20.f, 20.f, 1.f);
|
|
tmp->shader()->color = glm::vec4( 0.1f, 0.1f, 0.1f, 0.6f );
|
|
scene.bg()->attach(tmp);
|
|
// frame showing the source original shape
|
|
background_surface_= new Surface( new Shader);
|
|
background_surface_->scale_ = glm::vec3(20.f, 20.f, 1.f);
|
|
background_surface_->shader()->color = glm::vec4( COLOR_BGROUND, 1.0f );
|
|
scene.bg()->attach(background_surface_);
|
|
background_frame_ = new Frame(Frame::SHARP, Frame::THIN, Frame::NONE);
|
|
background_frame_->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 0.6f );
|
|
scene.bg()->attach(background_frame_);
|
|
// frame with checkerboard background to show cropped preview
|
|
preview_checker_ = new ImageSurface("images/checker.dds");
|
|
static glm::mat4 Tra = glm::scale(glm::translate(glm::identity<glm::mat4>(), glm::vec3( -32.f, -32.f, 0.f)), glm::vec3( 64.f, 64.f, 1.f));
|
|
preview_checker_->shader()->iTransform = Tra;
|
|
scene.bg()->attach(preview_checker_);
|
|
preview_frame_ = new Frame(Frame::SHARP, Frame::THIN, Frame::GLOW);
|
|
preview_frame_->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f );
|
|
scene.bg()->attach(preview_frame_);
|
|
// marks on the frame to show scale
|
|
show_scale_ = false;
|
|
horizontal_mark_ = new Mesh("mesh/h_mark.ply");
|
|
horizontal_mark_->translation_ = glm::vec3(0.f, -1.f, 0.0f);
|
|
horizontal_mark_->scale_ = glm::vec3(2.5f, -2.5f, 0.0f);
|
|
horizontal_mark_->rotation_.z = M_PI;
|
|
horizontal_mark_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f );
|
|
scene.bg()->attach(horizontal_mark_);
|
|
vertical_mark_ = new Mesh("mesh/h_mark.ply");
|
|
vertical_mark_->translation_ = glm::vec3(-1.0f, 0.0f, 0.0f);
|
|
vertical_mark_->scale_ = glm::vec3(2.5f, -2.5f, 0.0f);
|
|
vertical_mark_->rotation_.z = M_PI_2;
|
|
vertical_mark_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f );
|
|
scene.bg()->attach(vertical_mark_);
|
|
|
|
//
|
|
// surface to show the texture of the source
|
|
//
|
|
preview_shader_ = new ImageShader;
|
|
preview_surface_ = new Surface(preview_shader_); // to attach source preview
|
|
preview_surface_->translation_.z = 0.002f;
|
|
scene.bg()->attach(preview_surface_);
|
|
|
|
//
|
|
// User interface foreground
|
|
//
|
|
// Mask manipulation
|
|
mask_node_ = new Group;
|
|
mask_square_ = new Frame(Frame::SHARP, Frame::LARGE, Frame::NONE);
|
|
mask_square_->color = glm::vec4( COLOR_APPEARANCE_MASK, 1.f );
|
|
mask_node_->attach(mask_square_);
|
|
mask_circle_ = new Mesh("mesh/circle.ply");
|
|
mask_circle_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 1.f );
|
|
mask_node_->attach(mask_circle_);
|
|
mask_horizontal_ = new Mesh("mesh/h_line.ply");
|
|
mask_horizontal_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 1.f );
|
|
mask_horizontal_->scale_.x = 1.0f;
|
|
mask_horizontal_->scale_.y = 3.0f;
|
|
mask_node_->attach(mask_horizontal_);
|
|
mask_vertical_ = new Group;
|
|
Mesh *line = new Mesh("mesh/h_line.ply");
|
|
line->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 1.f );
|
|
line->scale_.x = 1.0f;
|
|
line->scale_.y = 3.0f;
|
|
line->rotation_.z = M_PI_2;
|
|
mask_vertical_->attach(line);
|
|
mask_node_->attach(mask_vertical_);
|
|
scene.fg()->attach(mask_node_);
|
|
|
|
// Source manipulation (texture coordinates)
|
|
//
|
|
// point to show POSITION
|
|
overlay_position_ = new Symbol(Symbol::SQUARE_POINT);
|
|
overlay_position_->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f );
|
|
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::CROSS);
|
|
overlay_position_cross_->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f );
|
|
overlay_position_cross_->rotation_ = glm::vec3(0.f, 0.f, M_PI_4);
|
|
overlay_position_cross_->scale_ = glm::vec3(0.3f, 0.3f, 1.f);
|
|
scene.fg()->attach(overlay_position_cross_);
|
|
overlay_position_cross_->visible_ = false;
|
|
// 'grid' : tic marks every 0.1 step for SCALING
|
|
// with dark background
|
|
Group *g = new Group;
|
|
Symbol *s = new Symbol(Symbol::GRID);
|
|
s->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f );
|
|
g->attach(s);
|
|
s = new Symbol(Symbol::SQUARE_POINT);
|
|
s->color = glm::vec4(0.f, 0.f, 0.f, 0.2f);
|
|
s->scale_ = glm::vec3(18.f, 18.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_->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f );
|
|
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_->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f );
|
|
overlay_scaling_->scale_ = glm::vec3(0.3f, 0.3f, 1.f);
|
|
scene.fg()->attach(overlay_scaling_);
|
|
overlay_scaling_->visible_ = false;
|
|
// 'clock' : tic marks every 10 degrees for ROTATION
|
|
// with dark background
|
|
g = new Group;
|
|
s = new Symbol(Symbol::CLOCK);
|
|
g->attach(s);
|
|
s = new Symbol(Symbol::CIRCLE_POINT);
|
|
s->color = glm::vec4(0.f, 0.f, 0.f, 0.25f);
|
|
s->scale_ = glm::vec3(28.f, 28.f, 1.f);
|
|
s->translation_.z = -0.1;
|
|
g->attach(s);
|
|
overlay_rotation_clock_ = g;
|
|
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_->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f );
|
|
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_->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f );
|
|
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_->color = glm::vec4( COLOR_APPEARANCE_SOURCE, 1.f );
|
|
overlay_rotation_->scale_ = glm::vec3(0.25f, 0.25f, 1.f);
|
|
scene.fg()->attach(overlay_rotation_);
|
|
overlay_rotation_->visible_ = false;
|
|
|
|
// Mask draw
|
|
mask_cursor_paint_ = 0;
|
|
mask_cursor_shape_ = 0;
|
|
stored_mask_size_ = glm::vec3(0.f);
|
|
mask_cursor_circle_ = new Mesh("mesh/icon_circle.ply");
|
|
mask_cursor_circle_->scale_ = glm::vec3(0.2f, 0.2f, 1.f);
|
|
mask_cursor_circle_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 0.8f );
|
|
mask_cursor_circle_->visible_ = false;
|
|
scene.fg()->attach(mask_cursor_circle_);
|
|
mask_cursor_square_ = new Mesh("mesh/icon_square.ply");
|
|
mask_cursor_square_->scale_ = glm::vec3(0.2f, 0.2f, 1.f);
|
|
mask_cursor_square_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 0.8f );
|
|
mask_cursor_square_->visible_ = false;
|
|
scene.fg()->attach(mask_cursor_square_);
|
|
mask_cursor_crop_ = new Mesh("mesh/icon_crop.ply");
|
|
mask_cursor_crop_->scale_ = glm::vec3(1.4f, 1.4f, 1.f);
|
|
mask_cursor_crop_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 0.9f );
|
|
mask_cursor_crop_->visible_ = false;
|
|
scene.fg()->attach(mask_cursor_crop_);
|
|
|
|
stored_mask_size_ = glm::vec3(0.f);
|
|
show_cursor_forced_ = false;
|
|
}
|
|
|
|
void AppearanceView::update(float dt)
|
|
{
|
|
View::update(dt);
|
|
|
|
// a more complete update is requested (e.g. after switching to view)
|
|
if (View::need_deep_update_ > 0 || edit_source_ != Mixer::manager().currentSource()) {
|
|
need_edit_update_ = true;
|
|
}
|
|
|
|
}
|
|
|
|
void AppearanceView::resize ( int scale )
|
|
{
|
|
float z = CLAMP(0.01f * (float) scale, 0.f, 1.f);
|
|
z *= z;
|
|
z *= APPEARANCE_MAX_SCALE - APPEARANCE_MIN_SCALE;
|
|
z += APPEARANCE_MIN_SCALE;
|
|
scene.root()->scale_.x = z;
|
|
scene.root()->scale_.y = z;
|
|
|
|
// Clamp translation to acceptable area
|
|
glm::vec3 border(scene.root()->scale_.x * 1.5, scene.root()->scale_.y * 1.5, 0.f);
|
|
scene.root()->translation_ = glm::clamp(scene.root()->translation_, -border, border);
|
|
}
|
|
|
|
int AppearanceView::size ()
|
|
{
|
|
float z = (scene.root()->scale_.x - APPEARANCE_MIN_SCALE) / (APPEARANCE_MAX_SCALE - APPEARANCE_MIN_SCALE);
|
|
return (int) ( sqrt(z) * 100.f);
|
|
}
|
|
|
|
void AppearanceView::select(glm::vec2 A, glm::vec2 B)
|
|
{
|
|
// unproject mouse coordinate into scene coordinates
|
|
glm::vec3 scene_point_A = Rendering::manager().unProject(A);
|
|
glm::vec3 scene_point_B = Rendering::manager().unProject(B);
|
|
|
|
// picking visitor traverses the scene
|
|
PickingVisitor pv(scene_point_A, scene_point_B, true); // here is the difference
|
|
scene.accept(pv);
|
|
|
|
// picking visitor found nodes in the area?
|
|
if ( !pv.empty()) {
|
|
|
|
// create a list of source matching the list of picked nodes
|
|
SourceList selection;
|
|
// loop over the nodes and add all sources found.
|
|
for(std::vector< std::pair<Node *, glm::vec2> >::const_reverse_iterator p = pv.rbegin(); p != pv.rend(); p++){
|
|
Source *s = Mixer::manager().findSource( p->first );
|
|
if (canSelect(s))
|
|
selection.push_back( s );
|
|
}
|
|
// set the selection with list of picked (overlaped) sources
|
|
Mixer::selection().set(selection);
|
|
}
|
|
else
|
|
// reset selection
|
|
Mixer::selection().clear();
|
|
}
|
|
|
|
bool AppearanceView::canSelect(Source *s) {
|
|
|
|
return ( s!=nullptr && ( s == Mixer::manager().currentSource() || s == edit_source_ ));
|
|
}
|
|
|
|
View::Cursor AppearanceView::over (glm::vec2 pos)
|
|
{
|
|
mask_cursor_circle_->visible_ = false;
|
|
mask_cursor_square_->visible_ = false;
|
|
mask_cursor_crop_->visible_ = false;
|
|
|
|
if (edit_source_ != nullptr)
|
|
{
|
|
glm::vec3 scene_pos = Rendering::manager().unProject(pos, scene.root()->transform_);
|
|
glm::vec2 P(scene_pos);
|
|
glm::vec2 S(preview_surface_->scale_);
|
|
mask_cursor_circle_->translation_ = glm::vec3(P, 0.f);
|
|
mask_cursor_square_->translation_ = glm::vec3(P, 0.f);
|
|
mask_cursor_crop_->translation_ = glm::vec3(P, 0.f);
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
if (!io.WantCaptureMouse || show_cursor_forced_)
|
|
{
|
|
// show paint brush cursor
|
|
if (edit_source_->maskShader()->mode == MaskShader::PAINT) {
|
|
if (mask_cursor_paint_ > 0) {
|
|
S += glm::vec2(Settings::application.brush.x);
|
|
if ( ABS(P.x) < S.x && ABS(P.y) < S.y ) {
|
|
mask_cursor_circle_->visible_ = Settings::application.brush.z < 1.0;
|
|
mask_cursor_square_->visible_ = Settings::application.brush.z > 0.0;
|
|
edit_source_->maskShader()->option = mask_cursor_paint_;
|
|
if (mask_cursor_paint_ > 1) {
|
|
ImVec4 c = ImGuiToolkit::HighlightColor(false);
|
|
mask_cursor_circle_->shader()->color = glm::vec4(c.x, c.y, c.z, 0.8f );
|
|
mask_cursor_square_->shader()->color = glm::vec4(c.x, c.y, c.z, 0.8f );
|
|
// mask_cursor_circle_->shader()->color = glm::vec4(COLOR_APPEARANCE_MASK_DISABLE, 0.9f );
|
|
// mask_cursor_square_->shader()->color = glm::vec4(COLOR_APPEARANCE_MASK_DISABLE, 0.9f );
|
|
} else {
|
|
ImVec4 c = ImGuiToolkit::HighlightColor();
|
|
mask_cursor_circle_->shader()->color = glm::vec4(c.x, c.y, c.z, 0.8f );
|
|
mask_cursor_square_->shader()->color = glm::vec4(c.x, c.y, c.z, 0.8f );
|
|
// mask_cursor_circle_->shader()->color = glm::vec4(COLOR_APPEARANCE_MASK, 0.9f );
|
|
// mask_cursor_square_->shader()->color = glm::vec4(COLOR_APPEARANCE_MASK, 0.9f );
|
|
}
|
|
}
|
|
else {
|
|
edit_source_->maskShader()->option = 0;
|
|
}
|
|
}
|
|
}
|
|
// show crop cursor
|
|
else if (edit_source_->maskShader()->mode == MaskShader::SHAPE) {
|
|
if (mask_cursor_shape_ > 0) {
|
|
mask_cursor_crop_->visible_ = true;
|
|
mask_cursor_crop_->scale_ = glm::vec3(1.4f /scene.root()->scale_.x, 1.4f / scene.root()->scale_.x, 1.f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Cursor();
|
|
}
|
|
|
|
std::pair<Node *, glm::vec2> AppearanceView::pick(glm::vec2 P)
|
|
{
|
|
// prepare empty return value
|
|
std::pair<Node *, glm::vec2> 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_, true);
|
|
scene.accept(pv);
|
|
|
|
// picking visitor found nodes?
|
|
if ( !pv.empty()) {
|
|
// keep edit source active if it is clicked
|
|
// AND if the cursor is not for drawing
|
|
Source *s = edit_source_;
|
|
if (s != nullptr) {
|
|
|
|
// special case for drawing in the mask
|
|
if ( s->maskShader()->mode == MaskShader::PAINT && mask_cursor_paint_ > 0) {
|
|
pick = { mask_cursor_circle_, P };
|
|
return pick;
|
|
}
|
|
// special case for cropping the mask shape
|
|
else if ( s->maskShader()->mode == MaskShader::SHAPE && mask_cursor_shape_ > 0) {
|
|
pick = { mask_cursor_crop_, P };
|
|
return pick;
|
|
}
|
|
|
|
// find if the edit 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( s ) ){
|
|
// a node in the current source was clicked !
|
|
pick = *itp;
|
|
break;
|
|
}
|
|
}
|
|
// not found: the edit source was not clicked
|
|
if ( itp == pv.rend() )
|
|
s = nullptr;
|
|
// picking on the menu handle
|
|
else if ( pick.first == s->handles_[mode_][Handles::MENU] ) {
|
|
// show context menu
|
|
openContextMenu();
|
|
}
|
|
}
|
|
|
|
// not the edit source (or no edit source)
|
|
if (s == nullptr) {
|
|
// cancel pick
|
|
pick = { nullptr, glm::vec2(0.f) };
|
|
}
|
|
|
|
}
|
|
|
|
return pick;
|
|
}
|
|
|
|
void AppearanceView::adjustBackground()
|
|
{
|
|
// by default consider edit source is null
|
|
mask_node_->visible_ = false;
|
|
float image_original_width = 1.f;
|
|
glm::vec3 scale = glm::vec3(1.f);
|
|
preview_surface_->setTextureIndex( Resource::getTextureTransparent() );
|
|
|
|
// if its a valid index
|
|
if (edit_source_ != nullptr) {
|
|
// update rendering frame to match edit source AR
|
|
image_original_width = edit_source_->frame()->aspectRatio();
|
|
scale = edit_source_->mixingsurface_->scale_;
|
|
preview_surface_->setTextureIndex( edit_source_->frame()->texture() );
|
|
preview_shader_->mask_texture = edit_source_->blendingShader()->mask_texture;
|
|
preview_surface_->scale_ = scale;
|
|
// mask appearance
|
|
mask_node_->visible_ = edit_source_->maskShader()->mode > MaskShader::PAINT && mask_cursor_shape_ > 0;
|
|
|
|
int shape = edit_source_->maskShader()->shape;
|
|
mask_circle_->visible_ = shape == MaskShader::ELIPSE;
|
|
mask_square_->visible_ = shape == MaskShader::OBLONG || shape == MaskShader::RECTANGLE;
|
|
mask_horizontal_->visible_ = shape == MaskShader::HORIZONTAL;
|
|
mask_vertical_->visible_ = shape == MaskShader::VERTICAL;
|
|
|
|
// symetrical shapes
|
|
if ( shape < MaskShader::HORIZONTAL){
|
|
mask_node_->scale_ = scale * glm::vec3(edit_source_->maskShader()->size, 1.f);
|
|
mask_node_->translation_ = glm::vec3(0.f);
|
|
}
|
|
// vertical
|
|
else if ( shape > MaskShader::HORIZONTAL ) {
|
|
mask_node_->scale_ = glm::vec3(1.f, scale.y, 1.f);
|
|
mask_node_->translation_ = glm::vec3(edit_source_->maskShader()->size.x * scale.x, 0.f, 0.f);
|
|
}
|
|
// horizontal
|
|
else {
|
|
mask_node_->scale_ = glm::vec3(scale.x, 1.f, 1.f);
|
|
mask_node_->translation_ = glm::vec3(0.f, edit_source_->maskShader()->size.y * scale.y, 0.f);
|
|
}
|
|
|
|
}
|
|
|
|
// background scene
|
|
background_surface_->scale_.x = image_original_width;
|
|
background_surface_->scale_.y = 1.f;
|
|
background_frame_->scale_.x = image_original_width;
|
|
vertical_mark_->translation_.x = -image_original_width;
|
|
preview_frame_->scale_ = scale;
|
|
preview_checker_->scale_ = scale;
|
|
glm::mat4 Ar = glm::scale(glm::identity<glm::mat4>(), scale );
|
|
static glm::mat4 Tra = glm::scale(glm::translate(glm::identity<glm::mat4>(), glm::vec3( -32.f, -32.f, 0.f)), glm::vec3( 64.f, 64.f, 1.f));
|
|
preview_checker_->shader()->iTransform = Ar * Tra;
|
|
|
|
}
|
|
|
|
Source *AppearanceView::getEditOrCurrentSource()
|
|
{
|
|
// cancel multiple selection
|
|
if (Mixer::selection().size() > 1) {
|
|
Source *s = Mixer::manager().currentSource();
|
|
Mixer::manager().unsetCurrentSource();
|
|
if ( s == nullptr )
|
|
s = Mixer::selection().front();
|
|
Mixer::selection().clear();
|
|
Mixer::manager().setCurrentSource(s);
|
|
}
|
|
|
|
// get current source
|
|
Source *_source = Mixer::manager().currentSource();
|
|
|
|
// no current source?
|
|
if (_source == nullptr) {
|
|
// if something can be selected
|
|
if ( !Mixer::manager().session()->empty()) {
|
|
// return the edit source, if exists
|
|
if (edit_source_ != nullptr)
|
|
_source = Mixer::manager().findSource(edit_source_->id());
|
|
}
|
|
}
|
|
|
|
return _source;
|
|
}
|
|
|
|
void AppearanceView::draw()
|
|
{
|
|
// edit view needs to be updated (source changed)
|
|
if ( need_edit_update_ )
|
|
{
|
|
need_edit_update_ = false;
|
|
|
|
// now, follow the change of current source
|
|
// & remember source to edit
|
|
edit_source_ = getEditOrCurrentSource();
|
|
|
|
// update background and frame to match editsource
|
|
adjustBackground();
|
|
}
|
|
|
|
// draw marks in axis
|
|
if (edit_source_ != nullptr && show_scale_){
|
|
if (edit_source_->maskShader()->shape != MaskShader::HORIZONTAL){
|
|
DrawVisitor dv(horizontal_mark_, Rendering::manager().Projection());
|
|
glm::vec3 dT = glm::vec3( -0.2f * edit_source_->mixingsurface_->scale_.x, 0.f, 0.f);
|
|
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), dT);
|
|
dv.loop(6, T);
|
|
scene.accept(dv);
|
|
dT = glm::vec3( +0.2f * edit_source_->mixingsurface_->scale_.x, 0.f, 0.f);
|
|
T = glm::translate(glm::identity<glm::mat4>(), dT);
|
|
dv.loop(6, T);
|
|
scene.accept(dv);
|
|
}
|
|
if (edit_source_->maskShader()->shape != MaskShader::VERTICAL){
|
|
DrawVisitor dv(vertical_mark_, Rendering::manager().Projection());
|
|
glm::vec3 dT = glm::vec3( 0.f, -0.2f * edit_source_->mixingsurface_->scale_.y, 0.f);
|
|
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), dT);
|
|
dv.loop(6, T);
|
|
scene.accept(dv);
|
|
dT = glm::vec3( 0.f, +0.2f * edit_source_->mixingsurface_->scale_.y, 0.f);
|
|
T = glm::translate(glm::identity<glm::mat4>(), dT);
|
|
dv.loop(6, T);
|
|
scene.accept(dv);
|
|
}
|
|
}
|
|
|
|
// draw general view
|
|
Shader::force_blending_opacity = true;
|
|
View::draw();
|
|
Shader::force_blending_opacity = false;
|
|
|
|
// if source active
|
|
if (edit_source_ != nullptr){
|
|
|
|
// force to redraw the frame of the edit source (even if source is not visible)
|
|
DrawVisitor dv(edit_source_->frames_[mode_], Rendering::manager().Projection(), true);
|
|
scene.accept(dv);
|
|
|
|
// display interface
|
|
// Locate window at upper left corner
|
|
glm::vec2 P = glm::vec2(-background_frame_->scale_.x - 0.02f, background_frame_->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("##AppearanceMaskOptions", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground
|
|
| ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings
|
|
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus ))
|
|
{
|
|
|
|
// style grey
|
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(COLOR_APPEARANCE_LIGHT, 1.0f)); // 1
|
|
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImVec4(0.14f, 0.14f, 0.14f, 0.9f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.14f, 0.14f, 0.14f, 0.f));
|
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.24f, 0.24f, 0.24f, 0.46f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImVec4(0.85f, 0.85f, 0.85f, 0.86f));
|
|
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, ImVec4(0.95f, 0.95f, 0.95f, 1.00f));
|
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.00f, 0.00f, 0.00f, 0.00f));
|
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.24f, 0.24f, 0.24f, 0.46f));
|
|
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.67f, 0.67f, 0.67f, 0.79f));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.36f, 0.36f, 0.36f, 0.44f));
|
|
ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.88f, 0.88f, 0.88f, 0.73f));
|
|
ImGui::PushStyleColor(ImGuiCol_Tab, ImVec4(0.83f, 0.83f, 0.84f, 0.78f));
|
|
ImGui::PushStyleColor(ImGuiCol_TabHovered, ImVec4(0.53f, 0.53f, 0.53f, 0.60f));
|
|
ImGui::PushStyleColor(ImGuiCol_TabActive, ImVec4(0.40f, 0.40f, 0.40f, 1.00f)); // 14 colors
|
|
|
|
int mode = edit_source_->maskShader()->mode;
|
|
ImGui::SetNextItemWidth( ImGui::GetTextLineHeight() * 2.6);
|
|
if ( ImGui::Combo("##Mask", &mode, MaskShader::mask_names, IM_ARRAYSIZE(MaskShader::mask_names) ) ) {
|
|
edit_source_->maskShader()->mode = mode;
|
|
if (mode == MaskShader::NONE)
|
|
Mixer::manager().setCurrentSource(edit_source_);
|
|
else if (mode == MaskShader::PAINT)
|
|
edit_source_->storeMask();
|
|
edit_source_->touch();
|
|
need_edit_update_ = true;
|
|
// store action history
|
|
std::ostringstream oss;
|
|
oss << edit_source_->name() << ": Mask " << (mode>1?"Shape":(mode>0?"Paint":"None"));
|
|
Action::manager().store(oss.str(), edit_source_->id());
|
|
}
|
|
|
|
// GUI for drawing mask
|
|
if (edit_source_->maskShader()->mode == MaskShader::PAINT) {
|
|
|
|
ImGui::SameLine();
|
|
ImGuiToolkit::HelpMarker( ICON_FA_EDIT "\tMask paint \n\n"
|
|
ICON_FA_MOUSE_POINTER "\t Edit texture\n"
|
|
ICON_FA_PAINT_BRUSH "\tBrush\n"
|
|
ICON_FA_ERASER "\tEraser\n\n"
|
|
ICON_FA_CARET_SQUARE_DOWN "\tBrush shape\n"
|
|
ICON_FA_DOT_CIRCLE "\tBrush size\n"
|
|
ICON_FA_FEATHER_ALT "\tBrush Pressure\n\n"
|
|
ICON_FA_MAGIC "\tEffects");
|
|
|
|
// select cursor
|
|
static bool on = true;
|
|
ImGui::SameLine(0, 60);
|
|
on = mask_cursor_paint_ == 0;
|
|
if (ImGuiToolkit::ButtonToggle(ICON_FA_MOUSE_POINTER, &on)) {
|
|
Mixer::manager().setCurrentSource(edit_source_);
|
|
mask_cursor_paint_ = 0;
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
on = mask_cursor_paint_ == 1;
|
|
if (ImGuiToolkit::ButtonToggle(ICON_FA_PAINT_BRUSH, &on)) {
|
|
Mixer::manager().unsetCurrentSource();
|
|
mask_cursor_paint_ = 1;
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
on = mask_cursor_paint_ == 2;
|
|
if (ImGuiToolkit::ButtonToggle(ICON_FA_ERASER, &on)) {
|
|
Mixer::manager().unsetCurrentSource();
|
|
mask_cursor_paint_ = 2;
|
|
}
|
|
|
|
if (mask_cursor_paint_ > 0) {
|
|
|
|
ImGui::SameLine(0, 50);
|
|
ImGui::SetNextItemWidth( ImGui::GetTextLineHeight() * 2.6);
|
|
const char* items[] = { ICON_FA_CIRCLE, ICON_FA_SQUARE };
|
|
static int item = 0;
|
|
item = (int) round(Settings::application.brush.z);
|
|
if(ImGui::Combo("##BrushShape", &item, items, IM_ARRAYSIZE(items))) {
|
|
Settings::application.brush.z = float(item);
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
show_cursor_forced_ = false;
|
|
if (ImGui::Button(ICON_FA_DOT_CIRCLE ICON_FA_SORT_DOWN ))
|
|
ImGui::OpenPopup("brush_size_popup");
|
|
if (ImGui::BeginPopup("brush_size_popup", ImGuiWindowFlags_NoMove))
|
|
{
|
|
int pixel_size_min = int(0.05 * edit_source_->frame()->height() );
|
|
int pixel_size_max = int(2.0 * edit_source_->frame()->height() );
|
|
int pixel_size = int(Settings::application.brush.x * edit_source_->frame()->height() );
|
|
show_cursor_forced_ = true;
|
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
|
|
ImGuiToolkit::HelpIcon("Large ", 16, 1, ICON_FA_CARET_SQUARE_RIGHT);
|
|
if (ImGui::VSliderInt("##BrushSize", ImVec2(30,260), &pixel_size, pixel_size_min, pixel_size_max, "") ){
|
|
Settings::application.brush.x = CLAMP(float(pixel_size) / edit_source_->frame()->height(), BRUSH_MIN_SIZE, BRUSH_MAX_SIZE);
|
|
}
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::BeginTooltip();
|
|
ImGui::Text("%d px", pixel_size);
|
|
ImGui::EndTooltip();
|
|
}
|
|
ImGuiToolkit::HelpIcon("Small ", 15, 1, ICON_FA_CARET_SQUARE_LEFT);
|
|
ImGui::PopFont();
|
|
ImGui::EndPopup();
|
|
}
|
|
// make sure the visual brush is up to date
|
|
glm::vec2 s = glm::vec2(Settings::application.brush.x);
|
|
mask_cursor_circle_->scale_ = glm::vec3(s * 1.16f, 1.f);
|
|
mask_cursor_square_->scale_ = glm::vec3(s * 1.75f, 1.f);
|
|
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(ICON_FA_FEATHER_ALT ICON_FA_SORT_DOWN ))
|
|
ImGui::OpenPopup("brush_pressure_popup");
|
|
if (ImGui::BeginPopup("brush_pressure_popup", ImGuiWindowFlags_NoMove))
|
|
{
|
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
|
|
ImGuiToolkit::HelpMarker("Light ", ICON_FA_FEATHER_ALT, ICON_FA_CARET_SQUARE_UP);
|
|
ImGui::VSliderFloat("##BrushPressure", ImVec2(30,260), &Settings::application.brush.y, BRUSH_MAX_PRESS, BRUSH_MIN_PRESS, "", 0.3f);
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::BeginTooltip();
|
|
ImGui::Text("%.1f%%", Settings::application.brush.y * 100.0);
|
|
ImGui::EndTooltip();
|
|
}
|
|
ImGuiToolkit::HelpMarker("Heavy ", ICON_FA_WEIGHT_HANGING, ICON_FA_CARET_SQUARE_DOWN);
|
|
ImGui::PopFont();
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
ImGui::SameLine(0, 60);
|
|
edit_source_->maskShader()->effect = 0;
|
|
if (ImGui::Button(ICON_FA_MAGIC ICON_FA_SORT_DOWN ))
|
|
ImGui::OpenPopup( "brush_menu_popup" );
|
|
if (ImGui::BeginPopup( "brush_menu_popup" ))
|
|
{
|
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
|
|
if (ImGui::Selectable( ICON_FA_BACKSPACE "\tClear")) {
|
|
edit_source_->maskShader()->effect = 1;
|
|
edit_source_->maskShader()->cursor = glm::vec4(100.0, 100.0, 0.f, 0.f);
|
|
edit_source_->touch();
|
|
// store action history
|
|
std::ostringstream oss;
|
|
oss << edit_source_->name() << ": Mask Paint Clear";
|
|
Action::manager().store(oss.str(), edit_source_->id());
|
|
}
|
|
if (ImGui::Selectable( ICON_FA_ADJUST "\tInvert")) {
|
|
edit_source_->maskShader()->effect = 2;
|
|
edit_source_->maskShader()->cursor = glm::vec4(100.0, 100.0, 0.f, 0.f);
|
|
edit_source_->touch();
|
|
// store action history
|
|
std::ostringstream oss;
|
|
oss << edit_source_->name() << ": Mask Paint Invert";
|
|
Action::manager().store(oss.str(), edit_source_->id());
|
|
}
|
|
if (ImGui::Selectable( ICON_FA_WAVE_SQUARE "\tEdge")) {
|
|
edit_source_->maskShader()->effect = 3;
|
|
edit_source_->maskShader()->cursor = glm::vec4(100.0, 100.0, 0.f, 0.f);
|
|
edit_source_->touch();
|
|
// store action history
|
|
std::ostringstream oss;
|
|
oss << edit_source_->name() << ": Mask Paint Edge";
|
|
Action::manager().store(oss.str(), edit_source_->id());
|
|
}
|
|
ImGui::PopFont();
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
|
|
}
|
|
// disabled info
|
|
else {
|
|
ImGui::SameLine(0, 60);
|
|
ImGui::TextDisabled( "Paint mask" );
|
|
}
|
|
|
|
}
|
|
// GUI for all other masks
|
|
else if (edit_source_->maskShader()->mode == MaskShader::SHAPE) {
|
|
|
|
ImGui::SameLine();
|
|
ImGuiToolkit::HelpMarker( ICON_FA_SHAPES "\tMask shape\n\n"
|
|
ICON_FA_MOUSE_POINTER "\t Edit texture\n"
|
|
ICON_FA_CROP_ALT "\tCrop & Edit shape\n\n"
|
|
ICON_FA_CARET_SQUARE_DOWN "\tShape of the mask\n"
|
|
ICON_FA_RADIATION_ALT "\tShape blur");
|
|
|
|
// select cursor
|
|
static bool on = true;
|
|
ImGui::SameLine(0, 60);
|
|
on = mask_cursor_shape_ == 0;
|
|
if (ImGuiToolkit::ButtonToggle(ICON_FA_MOUSE_POINTER, &on)) {
|
|
Mixer::manager().setCurrentSource(edit_source_);
|
|
need_edit_update_ = true;
|
|
mask_cursor_shape_ = 0;
|
|
}
|
|
|
|
ImGui::SameLine();
|
|
on = mask_cursor_shape_ == 1;
|
|
if (ImGuiToolkit::ButtonToggle(ICON_FA_CROP_ALT, &on)) {
|
|
Mixer::manager().unsetCurrentSource();
|
|
need_edit_update_ = true;
|
|
mask_cursor_shape_ = 1;
|
|
}
|
|
|
|
int shape = edit_source_->maskShader()->shape;
|
|
int blur_percent = int(edit_source_->maskShader()->blur * 100.f);
|
|
|
|
if (mask_cursor_shape_ > 0) {
|
|
|
|
ImGui::SameLine(0, 50);
|
|
ImGui::SetNextItemWidth( ImGui::GetTextLineHeight() * 6.5f);
|
|
if ( ImGui::Combo("##MaskShape", &shape, MaskShader::mask_shapes, IM_ARRAYSIZE(MaskShader::mask_shapes) ) ) {
|
|
edit_source_->maskShader()->shape = shape;
|
|
edit_source_->touch();
|
|
need_edit_update_ = true;
|
|
// store action history
|
|
std::ostringstream oss;
|
|
oss << edit_source_->name() << ": Mask Shape " << MaskShader::mask_shapes[shape];
|
|
Action::manager().store(oss.str(), edit_source_->id());
|
|
}
|
|
|
|
ImGui::SameLine(0, 20);
|
|
if (ImGui::Button(ICON_FA_RADIATION_ALT ICON_FA_SORT_DOWN ))
|
|
ImGui::OpenPopup("shape_smooth_popup");
|
|
if (ImGui::BeginPopup("shape_smooth_popup", ImGuiWindowFlags_NoMove))
|
|
{
|
|
static bool smoothchanged = false;
|
|
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_DEFAULT);
|
|
ImGuiToolkit::HelpIcon("Blur ", 7, 16, ICON_FA_CARET_SQUARE_UP);
|
|
if (ImGui::VSliderInt("##shapeblur", ImVec2(30,260), &blur_percent, 0, 100, "") ){
|
|
edit_source_->maskShader()->blur = float(blur_percent) / 100.f;
|
|
edit_source_->touch();
|
|
need_edit_update_ = true;
|
|
smoothchanged = true;
|
|
}
|
|
else if (smoothchanged && ImGui::IsMouseReleased(ImGuiMouseButton_Left)){
|
|
// store action history
|
|
std::ostringstream oss;
|
|
oss << edit_source_->name() << ": Mask Shape Blur " << blur_percent << "%";
|
|
Action::manager().store(oss.str(), edit_source_->id());
|
|
smoothchanged = false;
|
|
}
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::BeginTooltip();
|
|
ImGui::Text("%.d%%", blur_percent);
|
|
ImGui::EndTooltip();
|
|
}
|
|
ImGuiToolkit::HelpIcon("Sharp ", 8, 16, ICON_FA_CARET_SQUARE_DOWN);
|
|
ImGui::PopFont();
|
|
ImGui::EndPopup();
|
|
}
|
|
|
|
}
|
|
// disabled info
|
|
else {
|
|
ImGui::SameLine(0, 60);
|
|
ImGui::TextDisabled( MaskShader::mask_shapes[shape] );
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled( "mask");
|
|
}
|
|
}
|
|
else {// mode == MaskShader::NONE
|
|
ImGui::SameLine();
|
|
ImGuiToolkit::HelpMarker( ICON_FA_EXPAND "\tNo mask\n\n"
|
|
ICON_FA_MOUSE_POINTER "\t Edit texture\n");
|
|
// always active mouse pointer
|
|
ImGui::SameLine(0, 60);
|
|
bool on = true;
|
|
ImGuiToolkit::ButtonToggle(ICON_FA_MOUSE_POINTER, &on);
|
|
ImGui::SameLine(0, 60);
|
|
ImGui::TextDisabled( "No mask" );
|
|
}
|
|
|
|
ImGui::PopStyleColor(14); // 14 colors
|
|
ImGui::End();
|
|
}
|
|
ImGui::PopFont();
|
|
|
|
}
|
|
|
|
// display popup menu
|
|
if (show_context_menu_) {
|
|
ImGui::OpenPopup( "AppearanceContextMenu" );
|
|
show_context_menu_ = false;
|
|
}
|
|
showContextMenu(mode_,"AppearanceContextMenu");
|
|
|
|
show_scale_ = false;
|
|
}
|
|
|
|
#define MASK_PAINT_ACTION_LABEL "Mask Paint Edit"
|
|
|
|
View::Cursor AppearanceView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
|
|
{
|
|
std::ostringstream info;
|
|
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;
|
|
|
|
// Not grabbing a source
|
|
if (!s) {
|
|
// work on the edited source
|
|
if ( edit_source_ != nullptr ) {
|
|
|
|
if ( pick.first == mask_cursor_circle_ ) {
|
|
|
|
// inform shader of a cursor action : coordinates and crop scaling
|
|
edit_source_->maskShader()->cursor = glm::vec4(scene_to.x, scene_to.y,
|
|
edit_source_->mixingsurface_->scale_.x, edit_source_->mixingsurface_->scale_.y);
|
|
edit_source_->touch();
|
|
// action label
|
|
info << MASK_PAINT_ACTION_LABEL;
|
|
// cursor indication - no info, just cursor
|
|
ret.type = Cursor_Hand;
|
|
}
|
|
else if ( pick.first == mask_cursor_crop_ ) {
|
|
|
|
// special case for horizontal and vertical Shapes
|
|
bool hv = edit_source_->maskShader()->shape > MaskShader::RECTANGLE;
|
|
// match edit source AR
|
|
glm::vec3 val = edit_source_->mixingsurface_->scale_;
|
|
// use cursor translation to scale by quadrant
|
|
val = glm::sign( hv ? glm::vec3(1.f) : scene_from) * glm::vec3(scene_translation / val);
|
|
// relative change of stored mask size
|
|
val += stored_mask_size_;
|
|
// apply discrete scale with ALT modifier
|
|
if (UserInterface::manager().altModifier()) {
|
|
val.x = ROUND(val.x, 5.f);
|
|
val.y = ROUND(val.y, 5.f);
|
|
show_scale_ = true;
|
|
}
|
|
// Clamp | val | < 2.0
|
|
val = glm::sign(val) * glm::min( glm::abs(val), glm::vec3(2.f));
|
|
// clamp values for correct effect
|
|
if (edit_source_->maskShader()->shape == MaskShader::HORIZONTAL)
|
|
edit_source_->maskShader()->size.y = val.y;
|
|
else if (edit_source_->maskShader()->shape == MaskShader::VERTICAL)
|
|
edit_source_->maskShader()->size.x = val.x;
|
|
else
|
|
edit_source_->maskShader()->size = glm::max(glm::abs(glm::vec2(val)), glm::vec2(0.2));
|
|
// edit_source_->maskShader()->size = glm::max( glm::min( glm::vec2(val), glm::vec2(2.f)), glm::vec2(hv?-2.f:0.2f));
|
|
edit_source_->touch();
|
|
// update
|
|
need_edit_update_ = true;
|
|
// action label
|
|
info << "Texture Mask " << std::fixed << std::setprecision(3) << edit_source_->maskShader()->size.x;
|
|
info << " x " << edit_source_->maskShader()->size.y;
|
|
// cursor indication - no info, just cursor
|
|
ret.type = Cursor_Hand;
|
|
}
|
|
|
|
// store action in history
|
|
current_action_ = edit_source_->name() + ": " + info.str();
|
|
current_id_ = edit_source_->id();
|
|
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Group *sourceNode = s->group(mode_); // groups_[View::APPEARANCE]
|
|
|
|
// 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?
|
|
if (pick.first) {
|
|
// which corner was picked ?
|
|
glm::vec2 corner = glm::round(pick.second);
|
|
|
|
// transform from source 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 / s->frame()->aspectRatio(), 1.f, 1.f));
|
|
|
|
// transformation from scene to corner:
|
|
glm::mat4 scene_to_corner_transform = T * glm::inverse(s->stored_status_->transform_);
|
|
glm::mat4 corner_to_scene_transform = glm::inverse(scene_to_corner_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);
|
|
|
|
// convert source position in corner reference frame
|
|
glm::vec4 center = scene_to_corner_transform * glm::vec4( s->stored_status_->translation_, 1.f);
|
|
|
|
// picking on the resizing handles in the corners
|
|
if ( pick.first == s->handles_[mode_][Handles::RESIZE] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
|
|
// inform on which corner should be overlayed (opposite)
|
|
s->handles_[mode_][Handles::RESIZE]->overlayActiveCorner(-corner);
|
|
|
|
// RESIZE CORNER
|
|
// 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
|
|
sourceNode->scale_ = s->stored_status_->scale_ * glm::vec3(factor, factor, 1.f);
|
|
// discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.x = ROUND(sourceNode->scale_.x, 10.f);
|
|
factor = sourceNode->scale_.x / s->stored_status_->scale_.x;
|
|
sourceNode->scale_.y = s->stored_status_->scale_.y * factor;
|
|
}
|
|
// update corner scaling to apply to center coordinates
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
// non-proportional CORNER RESIZE (normal case)
|
|
else {
|
|
// scale node
|
|
sourceNode->scale_ = s->stored_status_->scale_ * corner_scaling;
|
|
// discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.x = ROUND(sourceNode->scale_.x, 10.f);
|
|
sourceNode->scale_.y = ROUND(sourceNode->scale_.y, 10.f);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
}
|
|
// 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_scene_transform * center;
|
|
// apply to node
|
|
sourceNode->translation_ = glm::vec3(center);
|
|
// show cursor depending on diagonal (corner picked)
|
|
T = glm::rotate(glm::identity<glm::mat4>(), s->stored_status_->rotation_.z, glm::vec3(0.f, 0.f, 1.f));
|
|
T = glm::scale(T, s->stored_status_->scale_);
|
|
corner = T * glm::vec4( corner, 0.f, 0.f );
|
|
ret.type = corner.x * corner.y > 0.f ? Cursor_ResizeNESW : Cursor_ResizeNWSE;
|
|
info << "Texture scale " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y;
|
|
|
|
}
|
|
// picking on the BORDER RESIZING handles left or right
|
|
else if ( pick.first == s->handles_[mode_][Handles::RESIZE_H] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
|
|
// inform on which corner should be overlayed (opposite)
|
|
s->handles_[mode_][Handles::RESIZE_H]->overlayActiveCorner(-corner);
|
|
|
|
// SHIFT: HORIZONTAL SCALE to restore source aspect ratio
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
sourceNode->scale_.x = ABS(sourceNode->scale_.y) * SIGN(sourceNode->scale_.x);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
// HORIZONTAL RESIZE (normal case)
|
|
else {
|
|
// x scale only
|
|
corner_scaling = glm::vec3(corner_scaling.x, 1.f, 1.f);
|
|
// scale node
|
|
sourceNode->scale_ = s->stored_status_->scale_ * corner_scaling;
|
|
// POST-CORRECTION ; discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.x = ROUND(sourceNode->scale_.x, 10.f);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
}
|
|
// 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_scene_transform * center;
|
|
// apply to node
|
|
sourceNode->translation_ = glm::vec3(center);
|
|
// show cursor depending on angle
|
|
float c = tan(sourceNode->rotation_.z);
|
|
ret.type = ABS(c) > 1.f ? Cursor_ResizeNS : Cursor_ResizeEW;
|
|
info << "Texture Scale " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y;
|
|
}
|
|
// picking on the BORDER RESIZING handles top or bottom
|
|
else if ( pick.first == s->handles_[mode_][Handles::RESIZE_V] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
|
|
// inform on which corner should be overlayed (opposite)
|
|
s->handles_[mode_][Handles::RESIZE_V]->overlayActiveCorner(-corner);
|
|
|
|
// SHIFT: VERTICAL SCALE to restore source aspect ratio
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
sourceNode->scale_.y = ABS(sourceNode->scale_.x) * SIGN(sourceNode->scale_.y);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
// VERTICAL RESIZE (normal case)
|
|
else {
|
|
// y scale only
|
|
corner_scaling = glm::vec3(1.f, corner_scaling.y, 1.f);
|
|
// scale node
|
|
sourceNode->scale_ = s->stored_status_->scale_ * corner_scaling;
|
|
// POST-CORRECTION ; discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.y = ROUND(sourceNode->scale_.y, 10.f);
|
|
corner_scaling = sourceNode->scale_ / s->stored_status_->scale_;
|
|
}
|
|
}
|
|
// 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_scene_transform * center;
|
|
// apply to node
|
|
sourceNode->translation_ = glm::vec3(center);
|
|
// show cursor depending on angle
|
|
float c = tan(sourceNode->rotation_.z);
|
|
ret.type = ABS(c) > 1.f ? Cursor_ResizeEW : Cursor_ResizeNS;
|
|
info << "Texture Scale " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y;
|
|
}
|
|
// picking on the CENTRER SCALING handle
|
|
else if ( pick.first == s->handles_[mode_][Handles::SCALE] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::ROTATE]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
|
|
// prepare overlay
|
|
overlay_scaling_cross_->visible_ = false;
|
|
overlay_scaling_grid_->visible_ = false;
|
|
overlay_scaling_->visible_ = true;
|
|
overlay_scaling_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_scaling_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_scaling_->rotation_.z = s->stored_status_->rotation_.z;
|
|
overlay_scaling_->update(0);
|
|
|
|
// PROPORTIONAL ONLY
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
float factor = glm::length( glm::vec2( source_to ) ) / glm::length( glm::vec2( source_from ) );
|
|
source_scaling = glm::vec3(factor, factor, 1.f);
|
|
overlay_scaling_cross_->visible_ = true;
|
|
overlay_scaling_cross_->copyTransform(overlay_scaling_);
|
|
}
|
|
// apply center scaling
|
|
sourceNode->scale_ = s->stored_status_->scale_ * source_scaling;
|
|
// POST-CORRECTION ; discretized scaling with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->scale_.x = ROUND(sourceNode->scale_.x, 10.f);
|
|
sourceNode->scale_.y = ROUND(sourceNode->scale_.y, 10.f);
|
|
overlay_scaling_grid_->visible_ = true;
|
|
overlay_scaling_grid_->copyTransform(overlay_scaling_);
|
|
}
|
|
// show cursor depending on diagonal
|
|
corner = glm::sign(sourceNode->scale_);
|
|
ret.type = (corner.x * corner.y) > 0.f ? Cursor_ResizeNWSE : Cursor_ResizeNESW;
|
|
info << "Texture Scale " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y;
|
|
}
|
|
// picking on the rotating handle
|
|
else if ( pick.first == s->handles_[mode_][Handles::ROTATE] ) {
|
|
|
|
// hide all other grips
|
|
s->handles_[mode_][Handles::RESIZE]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_H]->visible_ = false;
|
|
s->handles_[mode_][Handles::RESIZE_V]->visible_ = false;
|
|
s->handles_[mode_][Handles::SCALE]->visible_ = false;
|
|
s->handles_[mode_][Handles::MENU]->visible_ = false;
|
|
// ROTATION on CENTER
|
|
overlay_rotation_->visible_ = true;
|
|
overlay_rotation_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_rotation_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_rotation_->update(0);
|
|
overlay_rotation_fix_->visible_ = true;
|
|
overlay_rotation_fix_->copyTransform(overlay_rotation_);
|
|
overlay_rotation_clock_->visible_ = false;
|
|
|
|
// rotation center to center of source (disregarding scale)
|
|
glm::mat4 T = glm::translate(glm::identity<glm::mat4>(), s->stored_status_->translation_);
|
|
source_from = glm::inverse(T) * glm::vec4( scene_from, 1.f );
|
|
source_to = glm::inverse(T) * glm::vec4( scene_to, 1.f );
|
|
// compute rotation angle
|
|
float angle = glm::orientedAngle( glm::normalize(glm::vec2(source_from)), glm::normalize(glm::vec2(source_to)));
|
|
// apply rotation on Z axis
|
|
sourceNode->rotation_ = s->stored_status_->rotation_ + glm::vec3(0.f, 0.f, angle);
|
|
|
|
// POST-CORRECTION ; discretized rotation with ALT
|
|
int degrees = int( glm::degrees(sourceNode->rotation_.z) );
|
|
if (UserInterface::manager().altModifier()) {
|
|
degrees = (degrees / 10) * 10;
|
|
sourceNode->rotation_.z = glm::radians( float(degrees) );
|
|
overlay_rotation_clock_->visible_ = true;
|
|
overlay_rotation_clock_->copyTransform(overlay_rotation_);
|
|
info << "Texture Angle " << degrees << "\u00b0"; // degree symbol
|
|
}
|
|
else
|
|
info << "Texture Angle " << std::fixed << std::setprecision(1) << glm::degrees(sourceNode->rotation_.z) << "\u00b0"; // degree symbol
|
|
|
|
overlay_rotation_clock_hand_->visible_ = true;
|
|
overlay_rotation_clock_hand_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_rotation_clock_hand_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_rotation_clock_hand_->rotation_.z = sourceNode->rotation_.z;
|
|
overlay_rotation_clock_hand_->update(0);
|
|
|
|
// show cursor for rotation
|
|
ret.type = Cursor_Hand;
|
|
// + SHIFT = no scaling / NORMAL = with scaling
|
|
if (!UserInterface::manager().shiftModifier()) {
|
|
// compute scaling to match cursor
|
|
float factor = glm::length( glm::vec2( source_to ) ) / glm::length( glm::vec2( source_from ) );
|
|
source_scaling = glm::vec3(factor, factor, 1.f);
|
|
// apply center scaling
|
|
sourceNode->scale_ = s->stored_status_->scale_ * source_scaling;
|
|
info << std::endl << " Scale " << std::fixed << std::setprecision(3) << sourceNode->scale_.x;
|
|
info << " x " << sourceNode->scale_.y ;
|
|
overlay_rotation_fix_->visible_ = false;
|
|
}
|
|
}
|
|
// picking anywhere but on a handle: user wants to move the source
|
|
else {
|
|
ret.type = Cursor_ResizeAll;
|
|
sourceNode->translation_ = s->stored_status_->translation_ + scene_translation;
|
|
// discretized translation with ALT
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->translation_.x = ROUND(sourceNode->translation_.x, 10.f);
|
|
sourceNode->translation_.y = ROUND(sourceNode->translation_.y, 10.f);
|
|
}
|
|
// ALT: single axis movement
|
|
overlay_position_cross_->visible_ = false;
|
|
if (UserInterface::manager().shiftModifier()) {
|
|
overlay_position_cross_->visible_ = true;
|
|
overlay_position_cross_->translation_.x = s->stored_status_->translation_.x;
|
|
overlay_position_cross_->translation_.y = s->stored_status_->translation_.y;
|
|
overlay_position_cross_->update(0);
|
|
|
|
glm::vec3 dif = s->stored_status_->translation_ - sourceNode->translation_;
|
|
if (ABS(dif.x) > ABS(dif.y) ) {
|
|
sourceNode->translation_.y = s->stored_status_->translation_.y;
|
|
ret.type = Cursor_ResizeEW;
|
|
} else {
|
|
sourceNode->translation_.x = s->stored_status_->translation_.x;
|
|
ret.type = Cursor_ResizeNS;
|
|
}
|
|
}
|
|
// Show center overlay for POSITION
|
|
overlay_position_->visible_ = true;
|
|
overlay_position_->translation_.x = sourceNode->translation_.x;
|
|
overlay_position_->translation_.y = sourceNode->translation_.y;
|
|
overlay_position_->update(0);
|
|
// Show move cursor
|
|
info << "Texture Shift " << std::fixed << std::setprecision(3) << sourceNode->translation_.x;
|
|
info << ", " << sourceNode->translation_.y ;
|
|
}
|
|
}
|
|
|
|
// request update
|
|
s->touch();
|
|
|
|
// store action in history
|
|
current_action_ = s->name() + ": " + info.str();
|
|
current_id_ = s->id();
|
|
|
|
// update cursor
|
|
ret.info = info.str();
|
|
return ret;
|
|
}
|
|
|
|
|
|
void AppearanceView::initiate()
|
|
{
|
|
// View default initiation of action
|
|
View::initiate();
|
|
|
|
if ( edit_source_ != nullptr ) {
|
|
stored_mask_size_ = glm::vec3(edit_source_->maskShader()->size, 0.0);
|
|
|
|
// apply mask settings
|
|
edit_source_->maskShader()->brush = Settings::application.brush;
|
|
}
|
|
else
|
|
stored_mask_size_ = glm::vec3(0.f);
|
|
}
|
|
|
|
void AppearanceView::terminate()
|
|
{
|
|
// special case for texture paint: store image on mouse release (end of action PAINT)
|
|
if ( edit_source_ != nullptr && current_action_.find(MASK_PAINT_ACTION_LABEL) != std::string::npos ) {
|
|
edit_source_->storeMask();
|
|
edit_source_->maskShader()->cursor = glm::vec4(100.0, 100.0, 0.f, 0.f);
|
|
}
|
|
|
|
// View default termination of action
|
|
View::terminate();
|
|
|
|
// hide all overlays
|
|
overlay_position_->visible_ = false;
|
|
overlay_position_cross_->visible_ = false;
|
|
overlay_scaling_grid_->visible_ = false;
|
|
overlay_scaling_cross_->visible_ = false;
|
|
overlay_scaling_->visible_ = false;
|
|
overlay_rotation_clock_->visible_ = false;
|
|
overlay_rotation_clock_hand_->visible_ = false;
|
|
overlay_rotation_fix_->visible_ = false;
|
|
overlay_rotation_->visible_ = false;
|
|
|
|
// cancel 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::MENU]->visible_ = true;
|
|
}
|
|
|
|
}
|
|
|
|
void AppearanceView::arrow (glm::vec2 movement)
|
|
{
|
|
Source *s = Mixer::manager().currentSource();
|
|
if (s) {
|
|
|
|
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;
|
|
|
|
Group *sourceNode = s->group(mode_);
|
|
if (UserInterface::manager().altModifier()) {
|
|
sourceNode->translation_ += glm::vec3(movement.x, -movement.y, 0.f) * 0.1f;
|
|
sourceNode->translation_.x = ROUND(sourceNode->translation_.x, 10.f);
|
|
sourceNode->translation_.y = ROUND(sourceNode->translation_.y, 10.f);
|
|
}
|
|
else
|
|
sourceNode->translation_ += gl_delta * ARROWS_MOVEMENT_FACTOR;
|
|
|
|
// request update
|
|
s->touch();
|
|
}
|
|
else if (edit_source_) {
|
|
if (edit_source_->maskShader()->mode == MaskShader::PAINT) {
|
|
if (mask_cursor_paint_ > 0) {
|
|
glm::vec2 b = 0.05f * movement;
|
|
Settings::application.brush.x = CLAMP(Settings::application.brush.x+b.x, BRUSH_MIN_SIZE, BRUSH_MAX_SIZE);
|
|
Settings::application.brush.y = CLAMP(Settings::application.brush.y+b.y, BRUSH_MIN_PRESS, BRUSH_MAX_PRESS);
|
|
}
|
|
}
|
|
else if (edit_source_->maskShader()->mode == MaskShader::SHAPE) {
|
|
if (mask_cursor_shape_ > 0) {
|
|
float b = -0.05 * movement.y;
|
|
edit_source_->maskShader()->blur = CLAMP(edit_source_->maskShader()->blur+b, SHAPE_MIN_BLUR, SHAPE_MAX_BLUR);
|
|
edit_source_->touch();
|
|
}
|
|
}
|
|
}
|
|
}
|