From 34c24d99dfbbd4246ff4584e347371b4ac7865e9 Mon Sep 17 00:00:00 2001 From: Bruno Date: Sun, 27 Dec 2020 14:05:03 +0100 Subject: [PATCH] Integration procedural GLSL masks --- CMakeLists.txt | 3 + ImGuiVisitor.cpp | 44 +++-- ImGuiVisitor.h | 21 ++- ImageShader.cpp | 90 +++++++--- ImageShader.h | 32 +++- Primitives.cpp | 1 - SessionCreator.cpp | 84 +++------ SessionCreator.h | 1 + SessionVisitor.cpp | 20 ++- SessionVisitor.h | 1 + Shader.cpp | 16 +- Shader.h | 16 +- Source.cpp | 28 ++- Source.h | 9 + UserInterfaceManager.cpp | 2 +- View.cpp | 356 ++++++++++++++++++++++++++----------- View.h | 40 +++-- Visitor.h | 2 + rsc/images/mask_circle.png | Bin 31725 -> 32023 bytes rsc/shaders/image.fs | 2 +- 20 files changed, 510 insertions(+), 258 deletions(-) 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 ac105c6b3e3a55ef2b585c6c5038479a8764fb9d..01555e5f6c0d10caead39a5010233f93de95bad8 100644 GIT binary patch delta 4564 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 delta 4422 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 Mn>K|~v!x+&TQ*;Z+yDRo 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;