diff --git a/CMakeLists.txt b/CMakeLists.txt index b3a459f..29ad228 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,6 +290,8 @@ set(VMIX_RSC_FILES ./rsc/shaders/simple.fs ./rsc/shaders/simple.vs ./rsc/shaders/image.fs + ./rsc/shaders/mask_elipse.fs + ./rsc/shaders/mask_box.fs ./rsc/shaders/image.vs ./rsc/shaders/imageprocessing.fs ./rsc/fonts/Hack-Regular.ttf @@ -321,6 +323,7 @@ set(VMIX_RSC_FILES ./rsc/images/soft_shadow.dds ./rsc/mesh/disk.ply ./rsc/mesh/circle.ply + ./rsc/mesh/corner.ply ./rsc/mesh/shadow.ply ./rsc/mesh/glow.ply ./rsc/mesh/border_round.ply diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index 1b0e5c9..e36db02 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -161,6 +161,7 @@ void ImGuiVisitor::visit(Shader &n) { ImGui::PushID(std::to_string(n.id()).c_str()); + // Base color // if (ImGuiToolkit::ButtonIcon(10, 2)) { // n.blending = Shader::BLEND_OPACITY; // n.color = glm::vec4(1.f, 1.f, 1.f, 1.f); @@ -201,29 +202,26 @@ void ImGuiVisitor::visit(Shader &n) ImGui::PopID(); } -void ImGuiVisitor::visit(ImageShader &n) -{ - ImGui::PushID(std::to_string(n.id()).c_str()); - - // get index of the mask used in this ImageShader - int item_current = n.mask; - -// if (ImGuiToolkit::ButtonIcon(10, 3)) n.mask = 0; -// ImGui::SameLine(0, 10); - ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); - // combo list of masks - if ( ImGui::Combo("Mask", &item_current, ImageShader::mask_names, IM_ARRAYSIZE(ImageShader::mask_names) ) ) - { - if (item_current < (int) ImageShader::mask_presets.size()) - n.mask = item_current; - else { - // TODO ask for custom mask - } - Action::manager().store("Mask "+ std::string(ImageShader::mask_names[n.mask]), n.id()); - } - - ImGui::PopID(); -} +//void ImGuiVisitor::visit(ImageShader &n) +//{ +// ImGui::PushID(std::to_string(n.id()).c_str()); +// // get index of the mask used in this ImageShader +// int item_current = n.mask; +//// if (ImGuiToolkit::ButtonIcon(10, 3)) n.mask = 0; +//// ImGui::SameLine(0, 10); +// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); +// // combo list of masks +// if ( ImGui::Combo("Mask", &item_current, ImageShader::mask_names, IM_ARRAYSIZE(ImageShader::mask_names) ) ) +// { +// if (item_current < (int) ImageShader::mask_presets.size()) +// n.mask = item_current; +// else { +// // TODO ask for custom mask +// } +// Action::manager().store("Mask "+ std::string(ImageShader::mask_names[n.mask]), n.id()); +// } +// ImGui::PopID(); +//} void ImGuiVisitor::visit(ImageProcessingShader &n) { diff --git a/ImGuiVisitor.h b/ImGuiVisitor.h index 1508169..d1ee8ad 100644 --- a/ImGuiVisitor.h +++ b/ImGuiVisitor.h @@ -9,19 +9,18 @@ public: ImGuiVisitor(); // Elements of Scene - void visit(Scene& n) override; - void visit(Node& n) override; - void visit(Group& n) override; - void visit(Switch& n) override; - void visit(Primitive& n) override; - void visit(MediaSurface& n) override; - void visit(FrameBufferSurface& n) override; + void visit (Scene& n) override; + void visit (Node& n) override; + void visit (Group& n) override; + void visit (Switch& n) override; + void visit (Primitive& n) override; + void visit (MediaSurface& n) override; + void visit (FrameBufferSurface& n) override; // Elements with attributes - void visit(MediaPlayer& n) override; - void visit(Shader& n) override; - void visit(ImageShader& n) override; - void visit(ImageProcessingShader& n) override; + void visit (MediaPlayer& n) override; + void visit (Shader& n) override; + void visit (ImageProcessingShader& n) override; void visit (Source& s) override; void visit (MediaSource& s) override; void visit (SessionSource& s) override; diff --git a/ImageShader.cpp b/ImageShader.cpp index 5f96b9d..dd0c5c6 100644 --- a/ImageShader.cpp +++ b/ImageShader.cpp @@ -4,27 +4,16 @@ #include "Visitor.h" #include "ImageShader.h" #include "Resource.h" +#include "rsc/fonts/IconsFontAwesome5.h" +//#include static ShadingProgram imageShadingProgram("shaders/image.vs", "shaders/image.fs"); -const char* ImageShader::mask_names[11] = { "None", "Glow", "Halo", "Circle", "Round", "Vignette", "Top", "Botton", "Left", "Right", "Custom" }; -std::vector< uint > ImageShader::mask_presets; +const char* MaskShader::mask_names[3] = { ICON_FA_EXPAND, ICON_FA_CIRCLE, ICON_FA_SQUARE }; +std::vector< ShadingProgram* > MaskShader::mask_programs; -ImageShader::ImageShader(): Shader(), mask(0), custom_textureindex(0), stipple(0.0) +ImageShader::ImageShader(): Shader(), /*mask(0), custom_textureindex(0),*/ stipple(0.0) { - // first initialization - if ( mask_presets.empty() ) { - mask_presets.push_back(Resource::getTextureWhite()); - mask_presets.push_back(Resource::getTextureImage("images/mask_glow.png")); - mask_presets.push_back(Resource::getTextureImage("images/mask_halo.png")); - mask_presets.push_back(Resource::getTextureImage("images/mask_circle.png")); - mask_presets.push_back(Resource::getTextureImage("images/mask_roundcorner.png")); - mask_presets.push_back(Resource::getTextureImage("images/mask_vignette.png")); - mask_presets.push_back(Resource::getTextureImage("images/mask_linear_top.png")); - mask_presets.push_back(Resource::getTextureImage("images/mask_linear_bottom.png")); - mask_presets.push_back(Resource::getTextureImage("images/mask_linear_left.png")); - mask_presets.push_back(Resource::getTextureImage("images/mask_linear_right.png")); - } // static program shader program_ = &imageShadingProgram; // reset instance @@ -38,12 +27,9 @@ void ImageShader::use() program_->setUniform("stipple", stipple); glActiveTexture(GL_TEXTURE1); - if ( mask < 10 ) - glBindTexture(GL_TEXTURE_2D, mask_presets[mask]); - else - glBindTexture(GL_TEXTURE_2D, custom_textureindex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, mask_texture); glActiveTexture(GL_TEXTURE0); @@ -54,18 +40,16 @@ void ImageShader::reset() Shader::reset(); // default mask - mask = 0; - custom_textureindex = mask_presets[0]; + mask_texture = Resource::getTextureWhite(); // no stippling stipple = 0.f; } -void ImageShader::operator = (const ImageShader &S ) +void ImageShader::operator = (const ImageShader &S) { Shader::operator =(S); - mask = S.mask; - custom_textureindex = S.custom_textureindex; + mask_texture = S.mask_texture; stipple = S.stipple; } @@ -74,3 +58,59 @@ void ImageShader::accept(Visitor& v) { Shader::accept(v); v.visit(*this); } + + + + +MaskShader::MaskShader(): Shader(), mode(0) +{ + // first initialization + if ( mask_programs.empty() ) { + mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/simple.fs")); + mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_elipse.fs")); + mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_box.fs")); + } + // reset instance + reset(); + // static program shader + program_ = mask_programs[0]; +} + +void MaskShader::use() +{ + // select program to use + mode = CLAMP(mode, 0, mask_programs.size()-1); + program_ = mask_programs[mode]; + + // actual use of shader program + Shader::use(); + + // set parameters + program_->setUniform("blur", blur); + program_->setUniform("size", size); +} + +void MaskShader::reset() +{ + Shader::reset(); + + // default mask + mode = 0; + blur = 0.5f; + size = glm::vec2(1.f, 1.f); +} + +void MaskShader::operator = (const MaskShader &S) +{ + Shader::operator =(S); + + mode = S.mode; + blur = S.blur; + size = S.size; +} + + +void MaskShader::accept(Visitor& v) { + Shader::accept(v); + v.visit(*this); +} diff --git a/ImageShader.h b/ImageShader.h index 6ad0c0b..f944a69 100644 --- a/ImageShader.h +++ b/ImageShader.h @@ -20,15 +20,35 @@ public: void use() override; void reset() override; void accept(Visitor& v) override; - void operator = (const ImageShader &S); - uint mask; - uint custom_textureindex; - float stipple; + uint mask_texture; - static const char* mask_names[11]; - static std::vector< uint > mask_presets; + // uniforms + float stipple; +}; + + +class MaskShader : public Shader +{ + +public: + + MaskShader(); + + void use() override; + void reset() override; + void accept(Visitor& v) override; + void operator = (const MaskShader &S); + + uint mode; + + // uniforms + float blur; + glm::vec2 size; + + static const char* mask_names[3]; + static std::vector< ShadingProgram* > mask_programs; }; #endif // IMAGESHADER_H diff --git a/Primitives.cpp b/Primitives.cpp index abf5558..b7f7848 100644 --- a/Primitives.cpp +++ b/Primitives.cpp @@ -173,7 +173,6 @@ void MediaSurface::accept(Visitor& v) FrameBufferSurface::FrameBufferSurface(FrameBuffer *fb, Shader *s) : Surface(s), frame_buffer_(fb) { - } void FrameBufferSurface::draw(glm::mat4 modelview, glm::mat4 projection) diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 8e3d740..bf9a6a1 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -109,52 +109,6 @@ SessionLoader::SessionLoader(Session *session): Visitor(), session_(session) } -//Source *SessionLoader::createSource(XMLElement *sourceNode) -//{ -// // source to load -// Source *load_source = nullptr; - -// // check if a source with the given id exists in the session -// uint64_t id__ = 0; -// sourceNode->QueryUnsigned64Attribute("id", &id__); -// SourceList::iterator sit = session_->find(id__); - -// // no source with this id exists -// if ( sit == session_->end() ) { -// // create a new source depending on type -// const char *pType = sourceNode->Attribute("type"); -// if (!pType) -// continue; -// if ( std::string(pType) == "MediaSource") { -// load_source = new MediaSource; -// } -// else if ( std::string(pType) == "SessionSource") { -// load_source = new SessionSource; -// } -// else if ( std::string(pType) == "RenderSource") { -// load_source = new RenderSource(session_); -// } -// else if ( std::string(pType) == "PatternSource") { -// load_source = new PatternSource; -// } -// else if ( std::string(pType) == "DeviceSource") { -// load_source = new DeviceSource; -// } - -// // skip failed (including clones) -// if (!load_source) -// continue; - -// // add source to session -// session_->addSource(load_source); -// } -// // get reference to the existing source -// else -// load_source = *sit; - -// return load_source; -//} - void SessionLoader::load(XMLElement *sessionNode) { sources_id_.clear(); @@ -431,9 +385,24 @@ void SessionLoader::visit(ImageShader &n) XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms"); if (uniforms) { uniforms->QueryFloatAttribute("stipple", &n.stipple); - uniforms->QueryUnsignedAttribute("mask", &n.mask); } +} +void SessionLoader::visit(MaskShader &n) +{ + const char *pType = xmlCurrent_->Attribute("type"); + if ( std::string(pType) != "MaskShader" ) + return; + + xmlCurrent_->QueryUnsignedAttribute("mode", &n.mode); + + XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms"); + if (uniforms) { + uniforms->QueryFloatAttribute("blur", &n.blur); + XMLElement* size = uniforms->FirstChildElement("size"); + if (size) + tinyxml2::XMLElementToGLM( size->FirstChildElement("vec2"), n.size); + } } void SessionLoader::visit(ImageProcessingShader &n) @@ -474,24 +443,29 @@ void SessionLoader::visit (Source& s) s.setName(pName); xmlCurrent_ = sourceNode->FirstChildElement("Mixing"); - s.groupNode(View::MIXING)->accept(*this); + if (xmlCurrent_) s.groupNode(View::MIXING)->accept(*this); xmlCurrent_ = sourceNode->FirstChildElement("Geometry"); - s.groupNode(View::GEOMETRY)->accept(*this); + if (xmlCurrent_) s.groupNode(View::GEOMETRY)->accept(*this); xmlCurrent_ = sourceNode->FirstChildElement("Layer"); - s.groupNode(View::LAYER)->accept(*this); + if (xmlCurrent_) s.groupNode(View::LAYER)->accept(*this); xmlCurrent_ = sourceNode->FirstChildElement("Appearance"); - s.groupNode(View::APPEARANCE)->accept(*this); + if (xmlCurrent_) s.groupNode(View::APPEARANCE)->accept(*this); xmlCurrent_ = sourceNode->FirstChildElement("Blending"); - s.blendingShader()->accept(*this); + if (xmlCurrent_) s.blendingShader()->accept(*this); + + xmlCurrent_ = sourceNode->FirstChildElement("Mask"); + if (xmlCurrent_) s.maskShader()->accept(*this); xmlCurrent_ = sourceNode->FirstChildElement("ImageProcessing"); - bool on = xmlCurrent_->BoolAttribute("enabled", true); - s.processingShader()->accept(*this); - s.setImageProcessingEnabled(on); + if (xmlCurrent_) { + bool on = xmlCurrent_->BoolAttribute("enabled", true); + s.processingShader()->accept(*this); + s.setImageProcessingEnabled(on); + } // restore current xmlCurrent_ = sourceNode; diff --git a/SessionCreator.h b/SessionCreator.h index 32d5998..a1233fb 100644 --- a/SessionCreator.h +++ b/SessionCreator.h @@ -41,6 +41,7 @@ public: void visit (MediaPlayer& n) override; void visit (Shader& n) override; void visit (ImageShader& n) override; + void visit (MaskShader& n) override; void visit (ImageProcessingShader& n) override; // Sources diff --git a/SessionVisitor.cpp b/SessionVisitor.cpp index 917a29c..e83f79d 100644 --- a/SessionVisitor.cpp +++ b/SessionVisitor.cpp @@ -203,9 +203,23 @@ void SessionVisitor::visit(ImageShader &n) XMLElement *uniforms = xmlDoc_->NewElement("uniforms"); uniforms->SetAttribute("stipple", n.stipple); - uniforms->SetAttribute("mask", n.mask); xmlCurrent_->InsertEndChild(uniforms); +} +void SessionVisitor::visit(MaskShader &n) +{ + // Shader of a textured type + xmlCurrent_->SetAttribute("type", "MaskShader"); + xmlCurrent_->SetAttribute("id", n.id()); + xmlCurrent_->SetAttribute("mode", n.mode); + + XMLElement *uniforms = xmlDoc_->NewElement("uniforms"); + uniforms->SetAttribute("blur", n.blur); + XMLElement *size = xmlDoc_->NewElement("size"); + size->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.size) ); + uniforms->InsertEndChild(size); + + xmlCurrent_->InsertEndChild(uniforms); } void SessionVisitor::visit(ImageProcessingShader &n) @@ -351,6 +365,10 @@ void SessionVisitor::visit (Source& s) sourceNode->InsertEndChild(xmlCurrent_); s.blendingShader()->accept(*this); + xmlCurrent_ = xmlDoc_->NewElement( "Mask" ); + sourceNode->InsertEndChild(xmlCurrent_); + s.maskShader()->accept(*this); + xmlCurrent_ = xmlDoc_->NewElement( "ImageProcessing" ); xmlCurrent_->SetAttribute("enabled", s.imageProcessingEnabled()); sourceNode->InsertEndChild(xmlCurrent_); diff --git a/SessionVisitor.h b/SessionVisitor.h index 94b48e8..54814aa 100644 --- a/SessionVisitor.h +++ b/SessionVisitor.h @@ -38,6 +38,7 @@ public: void visit(MediaPlayer& n) override; void visit(Shader& n) override; void visit(ImageShader& n) override; + void visit(MaskShader& n) override; void visit(ImageProcessingShader& n) override; // Sources diff --git a/Shader.cpp b/Shader.cpp index 6cdb4d4..42a4475 100644 --- a/Shader.cpp +++ b/Shader.cpp @@ -1,5 +1,6 @@ #include "Shader.h" #include "Resource.h" +#include "FrameBuffer.h" #include "Log.h" #include "Visitor.h" #include "RenderingManager.h" @@ -119,14 +120,20 @@ void ShadingProgram::setUniform(const std::string& name, float val1, floa } template<> -void ShadingProgram::setUniform(const std::string& name, glm::vec4 val) { - glm::vec4 v(val); - glUniform4fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v)); +void ShadingProgram::setUniform(const std::string& name, glm::vec2 val) { + glm::vec2 v(val); + glUniform2fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v)); } template<> void ShadingProgram::setUniform(const std::string& name, glm::vec3 val) { glm::vec3 v(val); + glUniform3fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v)); +} + +template<> +void ShadingProgram::setUniform(const std::string& name, glm::vec4 val) { + glm::vec4 v(val); glUniform4fv(glGetUniformLocation(id_, name.c_str()), 1, glm::value_ptr(v)); } @@ -208,7 +215,7 @@ void Shader::use() program_->setUniform("iTransform", iTransform); program_->setUniform("color", color); - iResolution = glm::vec3( Rendering::manager().currentAttrib().viewport, 0.f); + glm::vec3 iResolution = glm::vec3( Rendering::manager().currentAttrib().viewport, 0.f); program_->setUniform("iResolution", iResolution); // Blending Function @@ -238,7 +245,6 @@ void Shader::reset() projection = glm::identity(); modelview = glm::identity(); iTransform = glm::identity(); - iResolution = glm::vec3(1280.f, 720.f, 0.f); color = glm::vec4(1.f, 1.f, 1.f, 1.f); } diff --git a/Shader.h b/Shader.h index 1458abb..0043548 100644 --- a/Shader.h +++ b/Shader.h @@ -7,6 +7,7 @@ // Forward declare classes referenced class Visitor; +class FrameBuffer; class ShadingProgram { @@ -22,13 +23,13 @@ public: static void enduse(); private: - void checkCompileErr(); - void checkLinkingErr(); - void compile(); - void link(); - unsigned int vertex_id_, fragment_id_, id_; - std::string vertex_code_; - std::string fragment_code_; + void checkCompileErr(); + void checkLinkingErr(); + void compile(); + void link(); + unsigned int vertex_id_, fragment_id_, id_; + std::string vertex_code_; + std::string fragment_code_; std::string vertex_file_; std::string fragment_file_; @@ -71,7 +72,6 @@ public: protected: ShadingProgram *program_; - glm::vec3 iResolution; }; diff --git a/Source.cpp b/Source.cpp index 3252b44..82de050 100644 --- a/Source.cpp +++ b/Source.cpp @@ -179,12 +179,18 @@ Source::Source() : initialized_(false), active_(true), need_update_(true), symbo // create objects stored_status_ = new Group; - // those will be associated to nodes later + // simple image shader (with texturing) for blending blendingshader_ = new ImageShader; + // mask produced by dedicated shader + maskshader_ = new MaskShader; + masksurface_ = new Surface(maskshader_); + + // filtered image shader (with texturing and processing) for rendering processingshader_ = new ImageProcessingShader; - // default to image processing enabled + // default rendering with image processing enabled renderingshader_ = (Shader *) processingshader_; + // for drawing in mixing view mixingshader_ = new ImageShader; mixingshader_->stipple = 1.0; @@ -198,6 +204,7 @@ Source::Source() : initialized_(false), active_(true), need_update_(true), symbo renderbuffer_ = nullptr; rendersurface_ = nullptr; mixingsurface_ = nullptr; + maskbuffer_ = nullptr; } @@ -213,6 +220,8 @@ Source::~Source() delete stored_status_; if (renderbuffer_) delete renderbuffer_; + if (maskbuffer_) + delete maskbuffer_; // all groups and their children are deleted in the scene // this includes rendersurface_, overlays, blendingshader_ and rendershader_ @@ -368,6 +377,11 @@ void Source::attach(FrameBuffer *renderbuffer) } } + // (re) create the masking buffer + if (maskbuffer_) + delete maskbuffer_; + maskbuffer_ = new FrameBuffer( renderbuffer->resolution() ); + // make the source visible if ( mode_ == UNINITIALIZED ) setMode(VISIBLE); @@ -410,7 +424,7 @@ void Source::update(float dt) dt_ = dt; // update nodes if needed - if (renderbuffer_ && mixingsurface_ && need_update_) + if (renderbuffer_ && mixingsurface_ && maskbuffer_ && need_update_) { // Log::Info("UPDATE %s %f", initials_, dt); @@ -472,9 +486,17 @@ void Source::update(float dt) // 7. switch back to UV coordinate system texturesurface_->shader()->iTransform = glm::inverse(UVtoScene) * glm::inverse(Sca) * glm::inverse(Ar) * Rot * Tra * Ar * UVtoScene; + // draw nask in mask frame buffer + maskbuffer_->begin(); + masksurface_->draw(glm::identity(), maskbuffer_->projection()); + maskbuffer_->end(); + // set the rendered mask as mask for blending + blendingshader_->mask_texture = maskbuffer_->texture(); + // do not update next frame need_update_ = false; } + } FrameBuffer *Source::frame() const diff --git a/Source.h b/Source.h index d2394ca..d182f58 100644 --- a/Source.h +++ b/Source.h @@ -11,6 +11,7 @@ #define DEFAULT_MIXING_TRANSLATION -1.f, 1.f class ImageShader; +class MaskShader; class ImageProcessingShader; class FrameBuffer; class FrameBufferSurface; @@ -77,6 +78,9 @@ public: // a Source has a shader to control mixing effects inline ImageShader *blendingShader () const { return blendingshader_; } + // a Source has a shader used to render mask + inline MaskShader *maskShader () const { return maskshader_; } + // a Source has a shader used to render in fbo inline Shader *renderingShader () const { return renderingshader_; } @@ -171,6 +175,11 @@ protected: ImageShader *blendingshader_; ImageShader *mixingshader_; + // shader and buffer to draw mask + MaskShader *maskshader_; + FrameBuffer *maskbuffer_; + Surface *masksurface_; + // surface to draw on Surface *texturesurface_; diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index ded5400..aec2e75 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -1976,7 +1976,7 @@ void Navigator::Render() } if (ImGui::IsItemHovered()) about = "Layers [F3]"; - if (ImGui::Selectable( ICON_FA_SIGN, &selected_view[4], 0, iconsize)) + if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE, &selected_view[4], 0, iconsize)) { Mixer::manager().setView(View::APPEARANCE); view_pannel_visible = previous_view == Settings::application.current_view; diff --git a/View.cpp b/View.cpp index 38abf44..0e7bcc6 100644 --- a/View.cpp +++ b/View.cpp @@ -823,14 +823,11 @@ 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_CROSSHAIRS " Center" )){ - s->group(m)->translation_ = glm::vec3(0.f); - s->touch(); - } - else if (ImGui::Selectable( ICON_FA_VECTOR_SQUARE " Reset" )){ + 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" )){ @@ -848,6 +845,10 @@ void showContextMenu(View::Mode m, const char* label) 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_ *= s->group(m)->crop_; @@ -1853,6 +1854,7 @@ AppearanceView::AppearanceView() : View(APPEARANCE), edit_source_(nullptr), need else restoreSettings(); + // // Scene background // // global dark @@ -1860,62 +1862,108 @@ AppearanceView::AppearanceView() : View(APPEARANCE), edit_source_(nullptr), need 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 - backgroundchecker_ = new ImageSurface("images/checker.dds"); + // 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::vec3( -32.f, -32.f, 0.f)), glm::vec3( 64.f, 64.f, 1.f)); - backgroundchecker_->shader()->iTransform = Tra; - scene.bg()->attach(backgroundchecker_); - backgroundframe_ = new Frame(Frame::SHARP, Frame::THIN, Frame::GLOW); - backgroundframe_->color = glm::vec4( COLOR_HIGHLIGHT_SOURCE, 1.f ); - scene.bg()->attach(backgroundframe_); - // Horizontal axis - horizontal_line_ = new Mesh("mesh/h_line.ply"); - horizontal_line_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f ); - horizontal_line_->translation_ = glm::vec3(0.f, 1.12f, 0.0f); - horizontal_line_->scale_.x = 1.0f; - horizontal_line_->scale_.y = 3.0f; - scene.bg()->attach(horizontal_line_); - show_horizontal_scale_ = false; - horizontal_mark_ = new Mesh("mesh/h_mark.ply"); - horizontal_mark_->translation_ = glm::vec3(0.f, 1.12f, 0.0f); - horizontal_mark_->scale_ = glm::vec3(2.5f, -2.5f, 0.0f); - horizontal_mark_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f ); - scene.bg()->attach(horizontal_mark_); - // vertical axis - vertical_line_ = new Group; - Mesh *line = new Mesh("mesh/h_line.ply"); - line->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f ); - line->translation_ = glm::vec3(-0.12f, 0.0f, 0.0f); - line->scale_.x = 1.0f; - line->scale_.y = 3.0f; - line->rotation_.z = M_PI_2; - vertical_line_->attach(line); - vertical_mark_ = new Mesh("mesh/h_mark.ply"); - vertical_mark_->translation_ = glm::vec3(-0.12f, 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 ); - vertical_line_->attach(vertical_mark_); - scene.bg()->attach(vertical_line_); + 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_); + // // surface to show the texture of the source - surfacepreview = new Surface; // to attach source preview - surfacepreview->translation_.z = 0.002f; - scene.bg()->attach(surfacepreview); + // + 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_); + + +// // Horizontal axis +// horizontal_line_ = new Mesh("mesh/h_line.ply"); +// horizontal_line_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f ); +// horizontal_line_->translation_ = glm::vec3(0.f, 1.12f, 0.0f); +// horizontal_line_->scale_.x = 1.0f; +// horizontal_line_->scale_.y = 3.0f; +//// scene.bg()->attach(horizontal_line_); +// show_horizontal_scale_ = false; +// horizontal_mark_ = new Mesh("mesh/h_mark.ply"); +// horizontal_mark_->translation_ = glm::vec3(0.f, 1.12f, 0.0f); +// horizontal_mark_->scale_ = glm::vec3(2.5f, -2.5f, 0.0f); +// horizontal_mark_->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f ); +//// scene.bg()->attach(horizontal_mark_); +// // vertical axis +// vertical_line_ = new Group; +// Mesh *line = new Mesh("mesh/h_line.ply"); +// line->shader()->color = glm::vec4( COLOR_TRANSITION_LINES, 0.9f ); +// line->translation_ = glm::vec3(-0.12f, 0.0f, 0.0f); +// line->scale_.x = 1.0f; +// line->scale_.y = 3.0f; +// line->rotation_.z = M_PI_2; +//// vertical_line_->attach(line); +// vertical_mark_ = new Mesh("mesh/h_mark.ply"); +// vertical_mark_->translation_ = glm::vec3(-0.12f, 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 ); +// vertical_line_->attach(vertical_mark_); +//// scene.bg()->attach(vertical_line_); + +/// Tests +// test_buffer = new FrameBuffer(800, 450); +// Log::Info("test_buffer %s", test_buffer->info().c_str()); +// test_shader = new MaskShader; +// test_shader->type = 0; +// test_shader->blur = 0.0; +// test_surface = new Surface(test_shader); +// preview_mask_ = new FrameBufferSurface(test_buffer); // to attach source preview +// preview_mask_->translation_.z = 0.002f; +//// scene.bg()->attach(preview_mask_); // Scene foreground // - // crop icons - crop_horizontal_ = new Symbol(Symbol::ARROWS); - crop_horizontal_->translation_ = glm::vec3(1.0f, 1.12f, 0.f); - scene.fg()->attach(crop_horizontal_); - crop_vertical_ = new Symbol(Symbol::ARROWS); - crop_vertical_->rotation_.z = M_PI_2; - crop_vertical_->translation_ = glm::vec3(-1.12f, -1.0f, 0.f); - scene.fg()->attach(crop_vertical_); +// // crop icons +// crop_horizontal_ = new Symbol(Symbol::ARROWS); +// crop_horizontal_->translation_ = glm::vec3(1.0f, 1.12f, 0.f); +//// scene.fg()->attach(crop_horizontal_); +// crop_vertical_ = new Symbol(Symbol::ARROWS); +// crop_vertical_->rotation_.z = M_PI_2; +// crop_vertical_->translation_ = glm::vec3(-1.12f, -1.0f, 0.f); +//// scene.fg()->attach(crop_vertical_); + // // User interface foreground // + + // Mask manipulation + mask_node_ = new Group; + mask_square_ = new Frame(Frame::SHARP, Frame::LARGE, Frame::NONE); + mask_square_->color = glm::vec4( 0.2f, 1.f, 1.f, 1.0f ); //BLUE + mask_node_->attach(mask_square_); + mask_handle_ = new Handles(Handles::CROP); + mask_handle_->color = glm::vec4( 0.2f, 1.f, 1.f, 1.0f ); //BLUE + mask_node_->attach(mask_handle_); + mask_circle_ = new Mesh("mesh/circle.ply"); + mask_circle_->shader()->color = glm::vec4( 0.2f, 1.f, 1.f, 1.0f ); + mask_node_->attach(mask_circle_); + mask_corner_ = new Mesh("mesh/corner.ply"); + mask_corner_->shader()->color = glm::vec4( 0.2f, 1.f, 1.f, 0.9f ); + mask_corner_->translation_ = glm::vec3(-1.f, -1.0f, 0.0f); + mask_node_->attach(mask_corner_); + + 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 ); @@ -2081,7 +2129,11 @@ std::pair AppearanceView::pick(glm::vec2 P) pick = *itp; break; } - else if ( (*itp).first == crop_horizontal_ || (*itp).first == crop_vertical_ ) { + // else if ( (*itp).first == crop_horizontal_ || (*itp).first == crop_vertical_ ) { + // pick = *itp; + // break; + // } + else if ( (*itp).first == mask_handle_ ) { pick = *itp; break; } @@ -2110,36 +2162,63 @@ std::pair AppearanceView::pick(glm::vec2 P) void AppearanceView::adjustBackground() { // by default consider edit source is null + mask_node_->visible_ = false; float image_original_width = 1.f; -// glm::vec2 image_crop_area = glm::vec2(1.f, 1.f); - surfacepreview->setTextureIndex( Resource::getTextureTransparent() ); + 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(); - surfacepreview->setTextureIndex( edit_source_->frame()->texture() ); - surfacepreview->scale_ = edit_source_->mixingsurface_->scale_; + 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_node_->visible_ = edit_source_->maskShader()->mode > 0; + mask_circle_->visible_ = edit_source_->maskShader()->mode == 1; + mask_square_->visible_ = edit_source_->maskShader()->mode == 2; + mask_node_->scale_ = scale * glm::vec3(edit_source_->maskShader()->size, 1.f); + mask_corner_->scale_.y = mask_node_->scale_.x / mask_node_->scale_.y; + +// crop_horizontal_->translation_.x = image_original_width * edit_source_->maskShader()->size.x; +// crop_vertical_->translation_.y = -edit_source_->maskShader()->size.y; +// crop_vertical_->translation_.x = -image_original_width - 0.12f; + + +///// Tests +// preview_mask_->scale_ = edit_source_->mixingsurface_->scale_; -// surfacepreview->scale_.x = image_original_width; -// image_crop_area.x *= image_original_width; } - // update objects in the scene to represent the image and crop area + + +// /// Tests +// // update mask +// test_buffer->begin(); +// test_surface->draw(glm::identity(), test_buffer->projection()); +// test_buffer->end(); + // background scene - horizontal_line_->scale_.x = image_original_width; - vertical_line_->translation_.x = -image_original_width; - backgroundframe_->scale_.x = image_original_width; - backgroundchecker_->scale_.x = image_original_width; - glm::mat4 Ar = glm::scale(glm::identity(), glm::vec3(image_original_width, 1.f, 1.f) ); - static glm::mat4 Tra = glm::scale(glm::translate(glm::identity(), glm::vec3( -32.f, -32.f, 0.f)), glm::vec3( 64.f, 64.f, 1.f)); - backgroundchecker_->shader()->iTransform = Ar * Tra; +// horizontal_line_->scale_.x = image_original_width; +// vertical_line_->translation_.x = -image_original_width; - // foreground -// crop_horizontal_->translation_.x = image_crop_area.x; -// crop_vertical_->translation_.y = -image_crop_area.y; -// crop_vertical_->translation_.x = -image_original_width - 0.12f; + background_surface_->scale_.x = image_original_width; + background_surface_->scale_.y = 1.f; + background_frame_->scale_.x = image_original_width; + preview_frame_->scale_ = scale; + preview_checker_->scale_ = scale; + glm::mat4 Ar = glm::scale(glm::identity(), scale ); + static glm::mat4 Tra = glm::scale(glm::translate(glm::identity(), glm::vec3( -32.f, -32.f, 0.f)), glm::vec3( 64.f, 64.f, 1.f)); + preview_checker_->shader()->iTransform = Ar * Tra; + +// backgroundchecker_->scale_.x = image_original_width; +// glm::mat4 Ar = glm::scale(glm::identity(), glm::vec3(image_original_width, 1.f, 1.f) ); +// static glm::mat4 Tra = glm::scale(glm::translate(glm::identity(), glm::vec3( -32.f, -32.f, 0.f)), glm::vec3( 64.f, 64.f, 1.f)); +// backgroundchecker_->shader()->iTransform = Ar * Tra; } @@ -2203,19 +2282,55 @@ void AppearanceView::draw() // } // } + // draw general view Shader::force_blending_opacity = true; View::draw(); Shader::force_blending_opacity = false; - // force to redraw the frame of the edit source (even if source is not visible) + // 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 + glm::vec2 P = Rendering::manager().project(glm::vec3(-background_frame_->scale_.x - 0.03f, 1.2f, 0.f), scene.root()->transform_, false); + ImGui::SetNextWindowPos(ImVec2(P.x, P.y), ImGuiCond_Always); + if (ImGui::Begin("##AppearanceMaskOptions", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground + | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) + { + ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE); + + int type = edit_source_->maskShader()->mode; + ImGui::SetNextItemWidth(100.f); + if ( ImGui::Combo("Mask ", &type, MaskShader::mask_names, IM_ARRAYSIZE(MaskShader::mask_names) ) ) { + edit_source_->maskShader()->mode = type; + edit_source_->touch(); + need_edit_update_ = true; + } + + if (edit_source_->maskShader()->mode > 0) { + int val = int(edit_source_->maskShader()->blur * 100.f); + ImGui::SameLine(); + ImGui::SetNextItemWidth(190.f); + if (ImGui::DragInt("Smooth", &val, 1, 0, 100, "%d%%") ) { + edit_source_->maskShader()->blur = float(val) / 100.f; + edit_source_->touch(); + need_edit_update_ = true; + } + } + + ImGui::PopFont(); + ImGui::End(); + } + } - show_vertical_scale_ = false; - show_horizontal_scale_ = false; +// show_vertical_scale_ = false; +// show_horizontal_scale_ = false; // display popup menu if (show_context_menu_) { @@ -2224,6 +2339,7 @@ void AppearanceView::draw() } showContextMenu(mode_,"AppearanceContextMenu"); + } View::Cursor AppearanceView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair pick) @@ -2241,44 +2357,70 @@ View::Cursor AppearanceView::grab (Source *s, glm::vec2 from, glm::vec2 to, std: // work on the edited source if ( edit_source_ != nullptr ) { - // picking on the resizing handles in the corners - if ( pick.first == crop_horizontal_ ) { - float max_width = edit_source_->frame()->aspectRatio(); - // discretized scaling with ALT - float val = scene_to.x; - if (UserInterface::manager().altModifier()) { - val = ROUND(val, 5.f); - show_horizontal_scale_ = true; - } - // crop horizontally -// edit_source_->texturesurface_->scale_.x = CLAMP(val, 0.2f, max_width) / max_width; -// edit_source_->touch(); -// // update background and frame -// adjustBackground(); -// // cursor indication -// info << "Crop " << std::fixed << std::setprecision(3) << max_width * edit_source_->texturesurface_->scale_.x; -// info << " x " << edit_source_->texturesurface_->scale_.y; - ret.type = Cursor_ResizeEW; - } - if ( pick.first == crop_vertical_ ) { - float max_width = edit_source_->frame()->aspectRatio(); - float val = -scene_to.y; + // match edit source AR +// float image_original_width = edit_source_->frame()->aspectRatio(); + glm::vec3 scale = edit_source_->mixingsurface_->scale_; + glm::vec3 delta = glm::vec3(0.1) / glm::vec3(scene.root()->scale_.x, scene.root()->scale_.y, 1.0); + + if ( pick.first == mask_handle_ ) { + // compute scaling of mask + glm::vec3 val = -scene_to + delta; + val /= scale; // discretized scaling with ALT if (UserInterface::manager().altModifier()) { - val = ROUND(val, 5.f); - show_vertical_scale_ = true; + val.x = ROUND(val.x, 5.f); + val.y = ROUND(val.y, 5.f); } - // crop vertically -// edit_source_->texturesurface_->scale_.y = CLAMP(val, 0.2f, 1.0f); -// edit_source_->touch(); -// // update background and frame -// adjustBackground(); -// // cursor indication -// info << "Crop " << std::fixed << std::setprecision(3) << max_width * edit_source_->texturesurface_->scale_.x ; -// info << " x " << edit_source_->texturesurface_->scale_.y; - ret.type = Cursor_ResizeNS; + // crop mask horizontally + edit_source_->maskShader()->size.x = CLAMP(val.x, 0.3f, 2.f); + edit_source_->maskShader()->size.y = CLAMP(val.y, 0.3f, 2.f); + edit_source_->touch(); + // update + need_edit_update_ = true; + // cursor indication + info << "Mask " << std::fixed << std::setprecision(3) << edit_source_->maskShader()->size.x; + info << " x " << edit_source_->maskShader()->size.y; + ret.type = Cursor_ResizeNESW; } +// // picking on the resizing handles in the corners +// if ( pick.first == crop_horizontal_ ) { +// float max_width = edit_source_->frame()->aspectRatio(); +// // discretized scaling with ALT +// float val = scene_to.x; +// if (UserInterface::manager().altModifier()) { +// val = ROUND(val, 5.f); +// show_horizontal_scale_ = true; +// } +// // crop mask horizontally +// edit_source_->maskShader()->size.x = CLAMP(val, 0.2f, max_width) / max_width; +// edit_source_->touch(); +// // update +// need_edit_update_ = true; +// // cursor indication +// info << "Mask " << std::fixed << std::setprecision(3) << max_width * edit_source_->maskShader()->size.x; +// info << " x " << edit_source_->maskShader()->size.y; +// ret.type = Cursor_ResizeEW; +// } +// if ( pick.first == crop_vertical_ ) { +// float max_width = edit_source_->frame()->aspectRatio(); +// float val = -scene_to.y; +// // discretized scaling with ALT +// if (UserInterface::manager().altModifier()) { +// val = ROUND(val, 5.f); +// show_vertical_scale_ = true; +// } +// // crop mask vertically +// edit_source_->maskShader()->size.y = CLAMP(val, 0.2f, 1.0f); +// edit_source_->touch(); +// // update +// need_edit_update_ = true; +// // cursor indication +// info << "Mask " << std::fixed << std::setprecision(3) << max_width * edit_source_->maskShader()->size.x ; +// info << " x " << edit_source_->maskShader()->size.y; +// ret.type = Cursor_ResizeNS; +// } + // store action in history current_action_ = edit_source_->name() + ": " + info.str(); current_id_ = edit_source_->id(); diff --git a/View.h b/View.h index 326df13..d15b211 100644 --- a/View.h +++ b/View.h @@ -249,17 +249,35 @@ private: Source *getEditOrCurrentSource(); void adjustBackground(); - Surface *surfacepreview; - Surface *backgroundchecker_; - Frame *backgroundframe_; - Mesh *horizontal_line_; - Mesh *horizontal_mark_; - bool show_horizontal_scale_; - Group *vertical_line_; - Mesh *vertical_mark_; - bool show_vertical_scale_; - Symbol *crop_horizontal_; - Symbol *crop_vertical_; + +// /// tests +// Surface *preview_mask_; +// Surface *test_surface; +// class MaskShader *test_shader; +// FrameBuffer *test_buffer; + + + Surface *preview_surface_; + class ImageShader *preview_shader_; + Surface *preview_checker_; + Frame *preview_frame_; + Surface *background_surface_; + Frame *background_frame_; +// Mesh *horizontal_line_; +// Mesh *horizontal_mark_; +// bool show_horizontal_scale_; +// Group *vertical_line_; +// Mesh *vertical_mark_; +// bool show_vertical_scale_; +// Symbol *crop_horizontal_; +// Symbol *crop_vertical_; + + Group *mask_node_; + Frame *mask_square_; + Mesh *mask_circle_; + Mesh *mask_corner_; + class Handles *mask_handle_; + Symbol *overlay_position_; Symbol *overlay_position_cross_; Symbol *overlay_scaling_; diff --git a/Visitor.h b/Visitor.h index 423bb42..ff2437d 100644 --- a/Visitor.h +++ b/Visitor.h @@ -25,6 +25,7 @@ class Stream; class MediaPlayer; class Shader; class ImageShader; +class MaskShader; class ImageProcessingShader; class Source; class MediaSource; @@ -64,6 +65,7 @@ public: virtual void visit (MediaPlayer&) {} virtual void visit (Shader&) {} virtual void visit (ImageShader&) {} + virtual void visit (MaskShader&) {} virtual void visit (ImageProcessingShader&) {} // utility diff --git a/rsc/images/mask_circle.png b/rsc/images/mask_circle.png index ac105c6..01555e5 100644 Binary files a/rsc/images/mask_circle.png and b/rsc/images/mask_circle.png differ diff --git a/rsc/shaders/image.fs b/rsc/shaders/image.fs index 750e92f..94a8699 100644 --- a/rsc/shaders/image.fs +++ b/rsc/shaders/image.fs @@ -7,7 +7,7 @@ in vec2 vertexUV; // from General Shader uniform vec3 iResolution; // viewport image resolution (in pixels) -uniform mat4 iTransform; // UV image transformation +uniform mat4 iTransform; // image transformation uniform vec4 color; uniform vec4 uv;