Mask Paint!! New MaskShader for mouse paiting of masks and associated changes.

UI for pain mask, load & save of FrameBuffer Image.
This commit is contained in:
Bruno
2021-01-10 14:52:57 +01:00
parent 3fc9401d97
commit 398995648a
25 changed files with 1437 additions and 170 deletions

2
.gitignore vendored
View File

@@ -16,3 +16,5 @@ rules.ninja
/vmix
/vimix_*.snap
/CMakeLists.txt.user.*
rsc/shaders/paint.fs

View File

@@ -298,8 +298,9 @@ set(VMIX_RSC_FILES
./rsc/shaders/mask_elipse.fs
./rsc/shaders/mask_box.fs
./rsc/shaders/mask_round.fs
./rsc/shaders/mask_lowleftcorner.fs
./rsc/shaders/mask_uprightcorner.fs
./rsc/shaders/mask_horizontal.fs
./rsc/shaders/mask_vertical.fs
./rsc/shaders/mask_draw.fs
./rsc/shaders/image.vs
./rsc/shaders/imageprocessing.fs
./rsc/fonts/Hack-Regular.ttf
@@ -373,6 +374,7 @@ set(VMIX_RSC_FILES
./rsc/mesh/icon_clock_hand.ply
./rsc/mesh/icon_grid.ply
./rsc/mesh/icon_rightarrow.ply
./rsc/mesh/icon_crop.ply
./rsc/mesh/h_line.ply
./rsc/mesh/h_mark.ply
)

View File

@@ -9,6 +9,8 @@
#include <glm/gtc/matrix_transform.hpp>
#include <glad/glad.h>
#include <stb_image.h>
#include <stb_image_write.h>
const char* FrameBuffer::aspect_ratio_name[5] = { "4:3", "3:2", "16:10", "16:9", "21:9" };
glm::vec2 FrameBuffer::aspect_ratio_size[5] = { glm::vec2(4.f,3.f), glm::vec2(3.f,2.f), glm::vec2(16.f,10.f), glm::vec2(16.f,9.f) , glm::vec2(21.f,9.f) };
@@ -129,6 +131,12 @@ FrameBuffer::~FrameBuffer()
{
if (framebufferid_)
glDeleteFramebuffers(1, &framebufferid_);
if (intermediate_framebufferid_)
glDeleteFramebuffers(1, &intermediate_framebufferid_);
if (textureid_)
glDeleteTextures(1, &textureid_);
if (intermediate_textureid_)
glDeleteTextures(1, &intermediate_textureid_);
}
@@ -163,7 +171,7 @@ glm::vec3 FrameBuffer::resolution() const
return glm::vec3(attrib_.viewport.x, attrib_.viewport.y, 0.f);
}
void FrameBuffer::begin()
void FrameBuffer::begin(bool clear)
{
if (!framebufferid_)
init();
@@ -172,7 +180,8 @@ void FrameBuffer::begin()
Rendering::manager().pushAttrib(attrib_);
glClear(GL_COLOR_BUFFER_BIT);
if (clear)
glClear(GL_COLOR_BUFFER_BIT);
}
void FrameBuffer::end()
@@ -197,7 +206,7 @@ void FrameBuffer::release()
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void FrameBuffer::readPixels()
void FrameBuffer::readPixels(uint8_t *target_data)
{
if (!framebufferid_)
return;
@@ -212,21 +221,24 @@ void FrameBuffer::readPixels()
else
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, attrib_.viewport.x, attrib_.viewport.y, (use_alpha_? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, 0);
glReadPixels(0, 0, attrib_.viewport.x, attrib_.viewport.y, (use_alpha_? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, target_data);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
bool FrameBuffer::blit(FrameBuffer *other)
bool FrameBuffer::blit(FrameBuffer *destination)
{
if (!framebufferid_ || !other || !other->framebufferid_)
if (!framebufferid_ || !destination)
return false;
if (!destination->framebufferid_)
destination->init();
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebufferid_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, other->framebufferid_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, destination->framebufferid_);
// blit to the frame buffer object
glBlitFramebuffer(0, 0, attrib_.viewport.x, attrib_.viewport.y,
0, 0, other->width(), other->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
0, 0, destination->width(), destination->height(), GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
@@ -285,3 +297,88 @@ void FrameBuffer::setProjectionArea(glm::vec2 c)
projection_ = glm::ortho(-projection_area_.x, projection_area_.x, projection_area_.y, -projection_area_.y, -1.f, 1.f);
}
FrameBufferImage::FrameBufferImage(int w, int h) :
rgb(nullptr), width(w), height(h)
{
if (width>0 && height>0)
rgb = new uint8_t[width*height*3];
}
FrameBufferImage::FrameBufferImage(jpegBuffer jpgimg) :
rgb(nullptr), width(0), height(0)
{
int c = 0;
if (jpgimg.buffer != nullptr && jpgimg.len >0)
rgb = stbi_load_from_memory(jpgimg.buffer, jpgimg.len, &width, &height, &c, 3);
}
FrameBufferImage::~FrameBufferImage() {
if (rgb!=nullptr)
delete rgb;
}
FrameBufferImage::jpegBuffer FrameBufferImage::getJpeg()
{
jpegBuffer jpgimg;
// if we hold a valid image
if (rgb!=nullptr && width>0 && height>0) {
// allocate JPEG buffer
// (NB: JPEG will need less than this but we can't know before...)
jpgimg.buffer = (unsigned char *) malloc( width * height * 3 * sizeof(unsigned char));
stbi_write_jpg_to_func( [](void *context, void *data, int size)
{
memcpy(((FrameBufferImage::jpegBuffer*)context)->buffer + ((FrameBufferImage::jpegBuffer*)context)->len, data, size);
((FrameBufferImage::jpegBuffer*)context)->len += size;
}
,&jpgimg, width, height, 3, rgb, FBI_JPEG_QUALITY);
}
return jpgimg;
}
FrameBufferImage *FrameBuffer::image(){
FrameBufferImage *img = nullptr;
// not ready
if (!framebufferid_)
return img;
// allocate image
img = new FrameBufferImage(attrib_.viewport.x, attrib_.viewport.y);
// get pixels into image
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); // set buffer target readpixel
readPixels(img->rgb);
return img;
}
bool FrameBuffer::fill(FrameBufferImage *image)
{
if (!framebufferid_)
init();
// not compatible for RGB
if (use_alpha_ || use_multi_sampling_)
return false;
// invalid image
if ( image == nullptr ||
image->rgb==nullptr ||
image->width !=attrib_.viewport.x ||
image->height!=attrib_.viewport.y )
return false;
// fill texture with image
glBindTexture(GL_TEXTURE_2D, textureid_);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height,
GL_RGB, GL_UNSIGNED_BYTE, image->rgb);
return true;
}

View File

@@ -1,9 +1,35 @@
#ifndef FRAMEBUFFER_H
#define FRAMEBUFFER_H
#include "Scene.h"
#include "RenderingManager.h"
#define FBI_JPEG_QUALITY 90
/**
* @brief The FrameBufferImage class stores an RGB image in RAM
* Direct access to rgb array, and exchange format to JPEG in RAM
*/
class FrameBufferImage
{
public:
uint8_t *rgb = nullptr;
int width;
int height;
struct jpegBuffer {
unsigned char *buffer = nullptr;
uint len = 0;
};
jpegBuffer getJpeg();
FrameBufferImage(int w, int h);
FrameBufferImage(jpegBuffer jpgimg);
~FrameBufferImage();
};
/**
* @brief The FrameBuffer class holds an OpenGL Frame Buffer Object.
*/
class FrameBuffer {
public:
@@ -22,14 +48,14 @@ public:
~FrameBuffer();
// Bind & push attribs to prepare draw
void begin();
void begin(bool clear = true);
// pop attrib and unbind to end draw
void end();
// blit copy to another, returns true on success
bool blit(FrameBuffer *other);
bool blit(FrameBuffer *destination);
// bind the FrameBuffer in READ and perform glReadPixels
// return the size of the buffer
void readPixels();
// (to be used after preparing a target PBO)
void readPixels(uint8_t* target_data = 0);
// clear color
inline void setClearColor(glm::vec4 color) { attrib_.clear_color = color; }
@@ -54,6 +80,10 @@ public:
// index for texturing
uint texture() const;
// get and fill image
FrameBufferImage *image();
bool fill(FrameBufferImage *image);
private:
void init();
void checkFramebufferStatus();
@@ -64,6 +94,7 @@ private:
uint textureid_, intermediate_textureid_;
uint framebufferid_, intermediate_framebufferid_;
bool use_alpha_, use_multi_sampling_;
};

View File

@@ -9,12 +9,8 @@
static ShadingProgram imageShadingProgram("shaders/image.vs", "shaders/image.fs");
const char* MaskShader::mask_names[6] = { ICON_FA_EXPAND,
ICON_FA_CIRCLE,
ICON_FA_MINUS_CIRCLE,
ICON_FA_SQUARE,
ICON_FA_CARET_SQUARE_RIGHT,
ICON_FA_CARET_SQUARE_LEFT };
const char* MaskShader::mask_names[3] = { ICON_FA_EXPAND, ICON_FA_EDIT, ICON_FA_SHAPES };
const char* MaskShader::mask_shapes[5] = { "Elipse", "Oblong", "Rectangle", "Horizontal", "Vertical" };
std::vector< ShadingProgram* > MaskShader::mask_programs;
ImageShader::ImageShader(): Shader(), stipple(0.0)
@@ -72,11 +68,12 @@ 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/image.vs", "shaders/mask_draw.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_round.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_box.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_lowleftcorner.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_uprightcorner.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_horizontal.fs"));
mask_programs.push_back(new ShadingProgram("shaders/simple.vs", "shaders/mask_vertical.fs"));
}
// reset instance
reset();
@@ -87,15 +84,22 @@ MaskShader::MaskShader(): Shader(), mode(0)
void MaskShader::use()
{
// select program to use
mode = CLAMP(mode, 0, mask_programs.size()-1);
program_ = mask_programs[mode];
mode = CLAMP(mode, 0, 2);
shape = CLAMP(shape, 0, 4);
program_ = mode < 2 ? mask_programs[mode] : mask_programs[shape+2] ;
// actual use of shader program
Shader::use();
// set parameters
program_->setUniform("blur", blur);
// shape parameters
size = shape < HORIZONTAL ? glm::max(glm::abs(size), glm::vec2(0.2)) : size;
program_->setUniform("size", size);
program_->setUniform("blur", blur);
// brush parameters
program_->setUniform("cursor", cursor);
program_->setUniform("brush", brush);
program_->setUniform("option", option);
}
void MaskShader::reset()
@@ -104,8 +108,16 @@ void MaskShader::reset()
// default mask
mode = 0;
// default shape
shape = 0;
blur = 0.5f;
size = glm::vec2(1.f, 1.f);
// default brush
cursor = glm::vec4(-10.f, -10.f, 1.f, 1.f);
brush = glm::vec3(0.5f, 0.1f, 0.f);
option = 0;
}
void MaskShader::operator = (const MaskShader &S)
@@ -113,6 +125,7 @@ void MaskShader::operator = (const MaskShader &S)
Shader::operator =(S);
mode = S.mode;
shape = S.shape;
blur = S.blur;
size = S.size;
}
@@ -122,3 +135,5 @@ void MaskShader::accept(Visitor& v) {
Shader::accept(v);
v.visit(*this);
}

View File

@@ -14,7 +14,6 @@ class ImageShader : public Shader
{
public:
ImageShader();
void use() override;
@@ -33,7 +32,6 @@ class MaskShader : public Shader
{
public:
MaskShader();
void use() override;
@@ -41,13 +39,32 @@ public:
void accept(Visitor& v) override;
void operator = (const MaskShader &S);
enum Modes {
NONE = 0,
PAINT = 1,
SHAPE = 2
};
uint mode;
// uniforms
float blur;
glm::vec2 size;
enum Shapes {
ELIPSE = 0,
OBLONG = 1,
RECTANGLE = 2,
HORIZONTAL = 3,
VERTICAL = 4
};
uint shape;
static const char* mask_names[6];
// uniforms
glm::vec2 size;
float blur;
int option;
glm::vec4 cursor;
glm::vec3 brush;
static const char* mask_names[3];
static const char* mask_shapes[5];
static std::vector< ShadingProgram* > mask_programs;
};

View File

@@ -75,7 +75,7 @@ void SessionCreator::load(const std::string& filename)
header->QueryIntAttribute("major", &version_major);
header->QueryIntAttribute("minor", &version_minor);
if (version_major != XML_VERSION_MAJOR || version_minor != XML_VERSION_MINOR){
Log::Warning("%s is in a different versions of session file. Loading might fail.", filename.c_str());
Log::Warning("%s is in an older versions of session file.\nLoading might fail or be incomplete.", filename.c_str());
// return;
}
@@ -100,6 +100,7 @@ void SessionCreator::loadConfig(XMLElement *viewsNode)
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Mixing"), *session_->config(View::MIXING));
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Geometry"), *session_->config(View::GEOMETRY));
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Layer"), *session_->config(View::LAYER));
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Appearance"), *session_->config(View::APPEARANCE));
SessionLoader::XMLToNode( viewsNode->FirstChildElement("Rendering"), *session_->config(View::RENDERING));
}
}
@@ -395,10 +396,12 @@ void SessionLoader::visit(MaskShader &n)
return;
xmlCurrent_->QueryUnsignedAttribute("mode", &n.mode);
xmlCurrent_->QueryUnsignedAttribute("shape", &n.shape);
XMLElement* uniforms = xmlCurrent_->FirstChildElement("uniforms");
if (uniforms) {
uniforms->QueryFloatAttribute("blur", &n.blur);
uniforms->QueryIntAttribute("option", &n.option);
XMLElement* size = uniforms->FirstChildElement("size");
if (size)
tinyxml2::XMLElementToGLM( size->FirstChildElement("vec2"), n.size);
@@ -458,7 +461,33 @@ void SessionLoader::visit (Source& s)
if (xmlCurrent_) s.blendingShader()->accept(*this);
xmlCurrent_ = sourceNode->FirstChildElement("Mask");
if (xmlCurrent_) s.maskShader()->accept(*this);
if (xmlCurrent_) {
// read the mask shader attributes
s.maskShader()->accept(*this);
// if there is an Image mask stored
XMLElement* imageNode = xmlCurrent_->FirstChildElement("Image");
if (imageNode) {
// if there is an internal array of data
XMLElement* array = imageNode->FirstChildElement("array");
if (array) {
// create a temporary jpeg with size of the array
FrameBufferImage::jpegBuffer jpgimg;
array->QueryUnsignedAttribute("len", &jpgimg.len);
// ok, we got a size of data to load
if (jpgimg.len>0) {
// allocate jpeg buffer
jpgimg.buffer = (unsigned char*) malloc(jpgimg.len);
// actual decoding of array
if (XMLElementDecodeArray(array, jpgimg.buffer, jpgimg.len) )
// create and set the image from jpeg
s.setMask(new FrameBufferImage(jpgimg));
// free temporary buffer
if (jpgimg.buffer)
free(jpgimg.buffer);
}
}
}
}
xmlCurrent_ = sourceNode->FirstChildElement("ImageProcessing");
if (xmlCurrent_) {

View File

@@ -208,17 +208,18 @@ void SessionVisitor::visit(ImageShader &n)
void SessionVisitor::visit(MaskShader &n)
{
// Shader of a textured type
// Shader of a mask type
xmlCurrent_->SetAttribute("type", "MaskShader");
xmlCurrent_->SetAttribute("id", n.id());
xmlCurrent_->SetAttribute("mode", n.mode);
xmlCurrent_->SetAttribute("shape", n.shape);
XMLElement *uniforms = xmlDoc_->NewElement("uniforms");
uniforms->SetAttribute("blur", n.blur);
uniforms->SetAttribute("option", n.option);
XMLElement *size = xmlDoc_->NewElement("size");
size->InsertEndChild( XMLElementFromGLM(xmlDoc_, n.size) );
uniforms->InsertEndChild(size);
xmlCurrent_->InsertEndChild(uniforms);
}
@@ -368,6 +369,28 @@ void SessionVisitor::visit (Source& s)
xmlCurrent_ = xmlDoc_->NewElement( "Mask" );
sourceNode->InsertEndChild(xmlCurrent_);
s.maskShader()->accept(*this);
// if we are saving a pain mask
if (s.maskShader()->mode == MaskShader::PAINT) {
// get the mask previously stored
FrameBufferImage *img = s.getMask();
if (img != nullptr) {
// get the jpeg encoded buffer
FrameBufferImage::jpegBuffer jpgimg = img->getJpeg();
if (jpgimg.buffer != nullptr) {
// fill the xml array with jpeg buffer
XMLElement *array = XMLElementEncodeArray(xmlDoc_, jpgimg.buffer, jpgimg.len);
// free the buffer
free(jpgimg.buffer);
// if we could create the array
if (array) {
// create an Image node to store the mask image
XMLElement *imageelement = xmlDoc_->NewElement("Image");
imageelement->InsertEndChild(array);
xmlCurrent_->InsertEndChild(imageelement);
}
}
}
}
xmlCurrent_ = xmlDoc_->NewElement( "ImageProcessing" );
xmlCurrent_->SetAttribute("enabled", s.imageProcessingEnabled());

View File

@@ -206,7 +206,8 @@ Source::Source() : initialized_(false), active_(true), need_update_(true), symbo
rendersurface_ = nullptr;
mixingsurface_ = nullptr;
maskbuffer_ = nullptr;
maskimage_ = nullptr;
mask_need_update_ = false;
}
@@ -223,6 +224,8 @@ Source::~Source()
delete renderbuffer_;
if (maskbuffer_)
delete maskbuffer_;
if (maskimage_)
delete maskimage_;
// all groups and their children are deleted in the scene
// this includes rendersurface_, overlays, blendingshader_ and rendershader_
@@ -381,7 +384,7 @@ void Source::attach(FrameBuffer *renderbuffer)
// (re) create the masking buffer
if (maskbuffer_)
delete maskbuffer_;
maskbuffer_ = new FrameBuffer( renderbuffer->resolution() );
maskbuffer_ = new FrameBuffer( glm::vec3(0.5) * renderbuffer->resolution() );
// make the source visible
if ( mode_ == UNINITIALIZED )
@@ -433,8 +436,8 @@ void Source::update(float dt)
// read position of the mixing node and interpret this as transparency of render output
glm::vec2 dist = glm::vec2(groups_[View::MIXING]->translation_);
// use the prefered transfer function
blendingshader_->color.a = sin_quad( dist.x, dist.y );
mixingshader_->color.a = blendingshader_->color.a;
blendingshader_->color = glm::vec4(1.0, 1.0, 1.0, sin_quad( dist.x, dist.y ));
mixingshader_->color = blendingshader_->color;
// CHANGE update status based on limbo
bool a = glm::length(dist) < 1.3f;
@@ -454,6 +457,7 @@ void Source::update(float dt)
// MODIFY CROP projection based on GEOMETRY crop
renderbuffer_->setProjectionArea( glm::vec2(groups_[View::GEOMETRY]->crop_) );
// Mixing and layer icons scaled based on GEOMETRY crop
mixingsurface_->scale_ = groups_[View::GEOMETRY]->crop_;
mixingsurface_->scale_.x *= renderbuffer_->aspectRatio();
@@ -487,10 +491,24 @@ 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<glm::mat4>(), maskbuffer_->projection());
maskbuffer_->end();
// if a mask image was given to be updated
if (mask_need_update_) {
// fill the mask buffer (once)
if (maskbuffer_->fill(maskimage_) )
mask_need_update_ = false;
}
// otherwise, render the mask buffer
else
{
// draw mask in mask frame buffer
maskbuffer_->begin(false);
// loopback maskbuffer texture for painting
masksurface_->setTextureIndex(maskbuffer_->texture());
// fill surface with mask texture
masksurface_->draw(glm::identity<glm::mat4>(), maskbuffer_->projection());
maskbuffer_->end();
}
// set the rendered mask as mask for blending
blendingshader_->mask_texture = maskbuffer_->texture();
@@ -522,6 +540,49 @@ bool Source::contains(Node *node) const
}
void Source::storeMask(FrameBufferImage *img)
{
// free the output mask storage
if (maskimage_ != nullptr) {
delete maskimage_;
maskimage_ = nullptr;
}
// if no image is provided
if (img == nullptr) {
// if ready
if (maskbuffer_!=nullptr) {
// get & store image from mask buffer
maskimage_ = maskbuffer_->image();
}
}
else
// store the given image
maskimage_ = img;
// maskimage_ can now be accessed with Source::getStoredMask
}
void Source::setMask(FrameBufferImage *img)
{
// if a valid image is given
if (img != nullptr && img->width>0 && img->height>0) {
// remember this new image as the current mask
// NB: will be freed when replaced
storeMask(img);
// ask Source::update to use it at next update for filling mask buffer
mask_need_update_ = true;
// ask to update the source
touch();
}
else
mask_need_update_ = false;
}
bool Source::hasNode::operator()(const Source* elem) const
{
if (_n && elem)

View File

@@ -77,16 +77,15 @@ 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_; }
// every Source has a frame buffer from the renderbuffer
virtual FrameBuffer *frame () const;
// a Source has a shader used to render mask
inline MaskShader *maskShader () const { return maskshader_; }
// touch to request update
inline void touch () { need_update_ = true; }
@@ -112,6 +111,11 @@ public:
// accept all kind of visitors
virtual void accept (Visitor& v);
// operations on mask
void storeMask(FrameBufferImage *img = nullptr);
FrameBufferImage *getMask() const { return maskimage_; }
void setMask(FrameBufferImage *img);
struct hasNode: public std::unary_function<Source*, bool>
{
bool operator()(const Source* elem) const;
@@ -179,6 +183,8 @@ protected:
MaskShader *maskshader_;
FrameBuffer *maskbuffer_;
Surface *masksurface_;
bool mask_need_update_;
FrameBufferImage *maskimage_;
// surface to draw on
Surface *texturesurface_;

View File

@@ -179,7 +179,6 @@ bool UserInterface::Init()
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.FontGlobalScale = Settings::application.scale;
// Setup Platform/Renderer bindings
@@ -447,10 +446,18 @@ void UserInterface::handleMouse()
if (ImGui::IsMouseClicked(ImGuiMouseButton_Right) /*|| ImGui::IsMouseClicked(ImGuiMouseButton_Middle)*/)
ImGui::FocusWindow(NULL);
//
// Mouse over
//
{
View::Cursor c = Mixer::manager().view()->over(mousepos);
if (c.type > 0)
setMouseCursor(io.MousePos, c);
}
// if not on any window
if ( !ImGui::IsAnyWindowHovered() && !ImGui::IsAnyWindowFocused() )
{
//
// Mouse wheel over background
//

433
View.cpp
View File

@@ -1681,14 +1681,14 @@ void TransitionView::draw()
scene.accept(dv2);
// display interface duration
glm::vec2 P = Rendering::manager().project(glm::vec3(-0.11f, -0.14f, 0.f), scene.root()->transform_, false);
glm::vec2 P = Rendering::manager().project(glm::vec3(-0.15f, -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_NoNav))
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus))
{
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
ImGui::SetNextItemWidth(100.f);
ImGui::SetNextItemWidth(160.f);
ImGui::DragFloat("##transitionduration", &Settings::application.transition.duration,
0.1f, TRANSITION_MIN_DURATION, TRANSITION_MAX_DURATION, "%.1f s");
ImGui::SameLine();
@@ -1912,21 +1912,43 @@ AppearanceView::AppearanceView() : View(APPEARANCE), edit_source_(nullptr), need
// 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.0f ); //BLUE
mask_square_->color = glm::vec4( COLOR_APPEARANCE_MASK, 1.f );
mask_node_->attach(mask_square_);
mask_handle_ = new Handles(Handles::CROP);
mask_handle_->color = glm::vec4( COLOR_APPEARANCE_MASK, 1.0f ); //BLUE
mask_node_->attach(mask_handle_);
mask_circle_ = new Mesh("mesh/circle.ply");
mask_circle_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 1.0f );
mask_circle_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 1.f );
mask_node_->attach(mask_circle_);
mask_corner_ = new Mesh("mesh/corner.ply");
mask_corner_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 0.9f );
mask_corner_->translation_ = glm::vec3(-1.f, -1.0f, 0.0f);
mask_corner_->scale_ = glm::vec3(0.5f, 0.5f, 1.f);
mask_node_->attach(mask_corner_);
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_);
// 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);
// Source manipulation (texture coordinates)
//
// point to show POSITION
@@ -2001,17 +2023,25 @@ AppearanceView::AppearanceView() : View(APPEARANCE), edit_source_(nullptr), need
scene.fg()->attach(overlay_rotation_);
overlay_rotation_->visible_ = false;
/// 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_);
// 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.2f, 1.2f, 1.f);
mask_cursor_crop_->shader()->color = glm::vec4( COLOR_APPEARANCE_MASK, 0.8f );
mask_cursor_crop_->visible_ = false;
scene.fg()->attach(mask_cursor_crop_);
}
@@ -2078,6 +2108,48 @@ void AppearanceView::select(glm::vec2 A, glm::vec2 B)
}
View::Cursor AppearanceView::over (glm::vec2 pos)
{
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);
mask_cursor_circle_->visible_ = false;
mask_cursor_square_->visible_ = false;
mask_cursor_crop_->visible_ = false;
ImGuiIO& io = ImGui::GetIO();
if (!io.WantCaptureMouse) {
// show paint brush cursor
if (edit_source_->maskShader()->mode == MaskShader::PAINT) {
if (mask_cursor_paint_ > 0) {
if ( ABS(P.x) < S.x && ABS(P.y) < S.y ) {
mask_cursor_circle_->visible_ = edit_source_->maskShader()->brush.z < 1.0;
mask_cursor_square_->visible_ = edit_source_->maskShader()->brush.z > 0.0;
edit_source_->maskShader()->option = mask_cursor_paint_;
}
else {
edit_source_->maskShader()->option = 0;
}
}
}
// show crup cursor
else if (edit_source_->maskShader()->mode == MaskShader::SHAPE) {
if (mask_cursor_shape_ > 0) {
mask_cursor_crop_->visible_ = true;
}
}
}
}
return Cursor();
}
std::pair<Node *, glm::vec2> AppearanceView::pick(glm::vec2 P)
{
// prepare empty return value
@@ -2093,9 +2165,21 @@ std::pair<Node *, glm::vec2> AppearanceView::pick(glm::vec2 P)
// 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++){
@@ -2106,10 +2190,6 @@ std::pair<Node *, glm::vec2> AppearanceView::pick(glm::vec2 P)
pick = *itp;
break;
}
else if ( (*itp).first == mask_handle_ ) {
pick = *itp;
break;
}
}
// not found: the edit source was not clicked
if ( itp == pv.rend() )
@@ -2149,18 +2229,29 @@ void AppearanceView::adjustBackground()
preview_shader_->mask_texture = edit_source_->blendingShader()->mask_texture;
preview_surface_->scale_ = scale;
// mask appearance
mask_node_->visible_ = edit_source_->maskShader()->mode > 0;
mask_circle_->visible_ = edit_source_->maskShader()->mode == 1;
mask_square_->visible_ = edit_source_->maskShader()->mode >= 2;
if (edit_source_->maskShader()->mode >= 4) {
mask_node_->scale_ = scale;
mask_node_->translation_ = scale - glm::vec3(edit_source_->maskShader()->size, 0.f) * scale ;
mask_node_->translation_.z = 0.f;
} else {
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);
}
mask_corner_->scale_.y = mask_corner_->scale_.x * mask_node_->scale_.x / mask_node_->scale_.y;
// 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
@@ -2174,11 +2265,6 @@ void AppearanceView::adjustBackground()
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;
// /// Tests
// // update mask
// test_buffer->begin();
// test_surface->draw(glm::identity<glm::mat4>(), test_buffer->projection());
// test_buffer->end();
}
Source *AppearanceView::getEditOrCurrentSource()
@@ -2226,17 +2312,25 @@ void AppearanceView::draw()
// 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);
DrawVisitor dv(horizontal_mark_, Rendering::manager().Projection());
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);
DrawVisitor dv(vertical_mark_, Rendering::manager().Projection());
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);
}
@@ -2260,40 +2354,181 @@ void AppearanceView::draw()
ImGui::SetNextWindowPos(ImVec2(P.x, P.y - 70.f ), ImGuiCond_Always);
if (ImGui::Begin("##AppearanceMaskOptions", NULL, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground
| ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav))
| ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBringToFrontOnFocus ))
{
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
int type = edit_source_->maskShader()->mode;
int mode = 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;
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() << ": Texture Mask " << type;
oss << edit_source_->name() << ": Texture Mask " << mode;
Action::manager().store(oss.str(), edit_source_->id());
}
if (edit_source_->maskShader()->mode > 0) {
int val = int(edit_source_->maskShader()->blur * 100.f);
static bool smoothchanged = false;
// GUI for drawing mask
if (edit_source_->maskShader()->mode == MaskShader::PAINT) {
// select cursor
static bool on = true;
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();
on = mask_cursor_paint_ == 0;
if (ImGuiToolkit::ButtonToggle(ICON_FA_MOUSE_POINTER, &on)) {
Mixer::manager().setCurrentSource(edit_source_);
mask_cursor_paint_ = 0;
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("Cursor");
ImGui::EndTooltip();
}
ImGui::SameLine();
on = mask_cursor_paint_ == 1;
if (ImGuiToolkit::ButtonToggle(ICON_FA_PAINT_BRUSH, &on)) {
Mixer::manager().unsetCurrentSource();
mask_cursor_paint_ = 1;
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("Paint");
ImGui::EndTooltip();
}
ImGui::SameLine();
on = mask_cursor_paint_ == 2;
if (ImGuiToolkit::ButtonToggle(ICON_FA_ERASER, &on)) {
Mixer::manager().unsetCurrentSource();
mask_cursor_paint_ = 2;
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("Erase");
ImGui::EndTooltip();
}
if (mask_cursor_paint_ > 0) {
ImGui::SameLine(0, 50);
ImGui::SetNextItemWidth(100.f);
const char* items[] = { ICON_FA_CIRCLE, ICON_FA_SQUARE };
static int item = 0;
item = (int) round(edit_source_->maskShader()->brush.z);
if(ImGui::Combo("##BrushShape", &item, items, IM_ARRAYSIZE(items))) {
edit_source_->maskShader()->brush.z = float(item);
}
ImGui::SameLine(0, 20);
ImGui::SetNextItemWidth(180.f);
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(edit_source_->maskShader()->brush.x *
edit_source_->frame()->height() );
if (ImGui::SliderInt("##BrushSize", &pixel_size, pixel_size_min, pixel_size_max, "%dpx") )
edit_source_->maskShader()->brush.x = CLAMP(float(pixel_size) / edit_source_->frame()->height(), 0.05, 2.0);
// ImGui::SliderFloat("Size", &edit_source_->maskShader()->brush.x, 0.1, 2.0, "%.1f");
glm::vec2 s = glm::vec2(edit_source_->maskShader()->brush.x);
mask_cursor_circle_->scale_ = glm::vec3(s * 1.16f, 1.f);
mask_cursor_square_->scale_ = glm::vec3(s * 1.72f, 1.f);
ImGui::SameLine(0, 20);
ImGui::SetNextItemWidth(150.f);
ImGui::SliderFloat("Strength", &edit_source_->maskShader()->brush.y, 0.01, 1.0, "%.2f", 3.f);
}
}
// GUI for all other masks
else if (edit_source_->maskShader()->mode == MaskShader::SHAPE) {
// select cursor
static bool on = true;
ImGui::SameLine();
on = mask_cursor_shape_ == 0;
if (ImGuiToolkit::ButtonToggle(ICON_FA_MOUSE_POINTER, &on)) {
Mixer::manager().setCurrentSource(edit_source_);
need_edit_update_ = true;
smoothchanged = true;
mask_cursor_shape_ = 0;
}
else if (smoothchanged && ImGui::IsMouseReleased(ImGuiMouseButton_Left)){
// store action history
std::ostringstream oss;
oss << edit_source_->name() << ": Texture Smooth " << edit_source_->maskShader()->blur;
Action::manager().store(oss.str(), edit_source_->id());
smoothchanged = false;
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("Cursor");
ImGui::EndTooltip();
}
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;
}
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("Crop");
ImGui::EndTooltip();
}
int shape = edit_source_->maskShader()->shape;
int val = int(edit_source_->maskShader()->blur * 100.f);
if (mask_cursor_shape_ > 0) {
ImGui::SameLine(0, 50);
ImGui::SetNextItemWidth(220.f);
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() << ": Texture Shape " << shape;
Action::manager().store(oss.str(), edit_source_->id());
}
static bool smoothchanged = false;
ImGui::SameLine(0, 20);
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;
smoothchanged = true;
}
else if (smoothchanged && ImGui::IsMouseReleased(ImGuiMouseButton_Left)){
// store action history
std::ostringstream oss;
oss << edit_source_->name() << ": Texture Smooth " << edit_source_->maskShader()->blur;
Action::manager().store(oss.str(), edit_source_->id());
smoothchanged = false;
}
}
// disabled info
else {
ImGui::SameLine(0, 60);
ImGui::TextDisabled( MaskShader::mask_shapes[shape] );
ImGui::SameLine(0, 180);
ImGui::TextDisabled( "%d%%", val );
ImGui::SameLine(0, 60);
ImGui::TextDisabled( "Smooth" );
}
}
else {// mode == MaskShader::NONE
// always active mouse pointer
bool on = true;
ImGui::SameLine();
ImGuiToolkit::ButtonToggle(ICON_FA_MOUSE_POINTER, &on);
}
ImGui::PopFont();
@@ -2312,6 +2547,8 @@ void AppearanceView::draw()
show_scale_ = false;
}
#define MASK_PAINT_ACTION_LABEL "Texture Paint"
View::Cursor AppearanceView::grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick)
{
std::ostringstream info;
@@ -2327,38 +2564,57 @@ View::Cursor AppearanceView::grab (Source *s, glm::vec2 from, glm::vec2 to, std:
// work on the edited source
if ( edit_source_ != nullptr ) {
// match edit source AR
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_cursor_circle_ ) {
if ( pick.first == mask_handle_ ) {
// compute scaling of mask
glm::vec3 val = -scene_to + delta;
val /= scale;
// discretized scaling with ALT
// 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;
}
// crop mask horizontally
edit_source_->maskShader()->size.x = CLAMP(val.x, 0.2f, 2.f);
edit_source_->maskShader()->size.y = CLAMP(val.y, 0.2f, 2.f);
// 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;
// cursor indication
// action label
info << "Texture Mask " << std::fixed << std::setprecision(3) << edit_source_->maskShader()->size.x;
info << " x " << edit_source_->maskShader()->size.y;
ret.type = Cursor_ResizeNESW;
// 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();
// update cursor
ret.info = info.str();
}
return ret;
}
@@ -2684,8 +2940,25 @@ View::Cursor AppearanceView::grab (Source *s, glm::vec2 from, glm::vec2 to, std:
}
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);
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();
}
// View default termination of action
View::terminate();
// hide all overlays

27
View.h
View File

@@ -71,10 +71,10 @@ public:
return Cursor();
}
// // test mouse over provided a point in screen coordinates and the picking point
// virtual Cursor over (Source*, glm::vec2, std::pair<Node *, glm::vec2>) {
// return Cursor();
// }
// test mouse over provided a point in screen coordinates
virtual Cursor over (glm::vec2) {
return Cursor();
}
// accessible scene
Scene scene;
@@ -240,6 +240,9 @@ public:
std::pair<Node *, glm::vec2> pick(glm::vec2 P) override;
Cursor grab (Source *s, glm::vec2 from, glm::vec2 to, std::pair<Node *, glm::vec2> pick) override;
Cursor drag (glm::vec2, glm::vec2) override;
Cursor over (glm::vec2) override;
void initiate() override;
void terminate() override;
private:
@@ -261,8 +264,8 @@ private:
Group *mask_node_;
Frame *mask_square_;
Mesh *mask_circle_;
Mesh *mask_corner_;
class Handles *mask_handle_;
Mesh *mask_horizontal_;
Group *mask_vertical_;
Symbol *overlay_position_;
Symbol *overlay_position_cross_;
@@ -275,12 +278,14 @@ private:
Symbol *overlay_rotation_clock_hand_;
bool show_context_menu_;
// for mask shader draw: 0=cursor, 1=brush, 2=eraser, 3=crop_shape
int mask_cursor_paint_;
int mask_cursor_shape_;
Mesh *mask_cursor_circle_;
Mesh *mask_cursor_square_;
Mesh *mask_cursor_crop_;
glm::vec3 stored_mask_size_;
// /// tests
// Surface *preview_mask_;
// Surface *test_surface;
// class MaskShader *test_shader;
// FrameBuffer *test_buffer;
};

View File

@@ -7,7 +7,7 @@
#define APP_VERSION_MAJOR 0
#define APP_VERSION_MINOR 4
#define XML_VERSION_MAJOR 0
#define XML_VERSION_MINOR 1
#define XML_VERSION_MINOR 2
#define MAX_RECENT_HISTORY 20
#define MINI(a, b) (((a) < (b)) ? (a) : (b))

506
rsc/mesh/icon_crop.ply Normal file
View File

@@ -0,0 +1,506 @@
ply
format ascii 1.0
comment Created by Blender 2.91.0 - www.blender.org
element vertex 250
property float x
property float y
property float z
element face 246
property list uchar uint vertex_indices
end_header
-0.042157 0.063218 0.000000
-0.041120 0.063272 0.000000
-0.041354 0.063272 0.000000
-0.040476 0.063272 0.000000
-0.039503 0.063272 0.000000
-0.038283 0.063272 0.000000
-0.036898 0.063272 0.000000
-0.035432 0.063272 0.000000
-0.033965 0.063272 0.000000
-0.032581 0.063272 0.000000
-0.031361 0.063272 0.000000
-0.030387 0.063272 0.000000
-0.029743 0.063272 0.000000
-0.029510 0.063272 0.000000
-0.028707 0.063218 0.000000
-0.042927 0.063061 0.000000
-0.027936 0.063061 0.000000
-0.043658 0.062807 0.000000
-0.027206 0.062807 0.000000
-0.044342 0.062464 0.000000
-0.026522 0.062464 0.000000
-0.044972 0.062039 0.000000
-0.025892 0.062039 0.000000
-0.045540 0.061539 0.000000
-0.025323 0.061539 0.000000
-0.046041 0.060970 0.000000
-0.024823 0.060970 0.000000
-0.046467 0.060341 0.000000
-0.024397 0.060341 0.000000
-0.046810 0.059657 0.000000
-0.024054 0.059657 0.000000
-0.047064 0.058926 0.000000
-0.023800 0.058926 0.000000
-0.047221 0.058155 0.000000
-0.023642 0.058155 0.000000
-0.047275 0.057352 0.000000
-0.023588 0.057352 0.000000
-0.047275 0.057158 0.000000
-0.023588 0.055760 0.000000
-0.047275 0.056621 0.000000
-0.047275 0.055810 0.000000
-0.047275 0.054794 0.000000
-0.023588 0.051358 0.000000
-0.047275 0.053640 0.000000
-0.047275 0.052418 0.000000
-0.047275 0.051196 0.000000
-0.023588 0.044709 0.000000
-0.047275 0.050043 0.000000
-0.047275 0.049026 0.000000
-0.047275 0.048215 0.000000
-0.047275 0.047678 0.000000
-0.047275 0.047484 0.000000
-0.057948 0.047430 0.000000
-0.057145 0.047484 0.000000
-0.015693 0.023802 0.000000
-0.014605 0.047484 0.000000
-0.015693 0.047484 0.000000
-0.011599 0.047484 0.000000
-0.007057 0.047484 0.000000
-0.001363 0.047484 0.000000
0.005097 0.047484 0.000000
0.011942 0.047484 0.000000
0.018787 0.047484 0.000000
0.025248 0.047484 0.000000
0.030941 0.047484 0.000000
0.035483 0.047484 0.000000
0.038490 0.047484 0.000000
0.039577 0.047484 0.000000
0.040648 0.047412 0.000000
-0.058719 0.047273 0.000000
0.041676 0.047202 0.000000
-0.059449 0.047019 0.000000
0.042650 0.046864 0.000000
-0.060133 0.046676 0.000000
0.043562 0.046406 0.000000
-0.060763 0.046251 0.000000
0.044402 0.045839 0.000000
-0.061332 0.045751 0.000000
0.045160 0.045172 0.000000
-0.061832 0.045182 0.000000
-0.062258 0.044553 0.000000
0.045827 0.044414 0.000000
-0.023588 0.036374 0.000000
-0.062601 0.043869 0.000000
0.046395 0.043574 0.000000
-0.062855 0.043138 0.000000
0.046852 0.042663 0.000000
-0.063013 0.042367 0.000000
0.047191 0.041689 0.000000
-0.063067 0.041564 0.000000
0.047401 0.040661 0.000000
-0.063067 0.041331 0.000000
-0.063067 0.040687 0.000000
-0.063067 0.039714 0.000000
0.047473 0.039590 0.000000
-0.063067 0.038494 0.000000
0.023786 0.023802 0.000000
0.047473 -0.023562 0.000000
-0.063067 0.037110 0.000000
-0.063067 0.035643 0.000000
-0.023588 0.026916 0.000000
-0.063067 0.034177 0.000000
-0.063067 0.032793 0.000000
-0.063067 0.031573 0.000000
-0.063067 0.030600 0.000000
-0.063067 0.029956 0.000000
-0.063067 0.029723 0.000000
-0.063013 0.028920 0.000000
-0.062855 0.028149 0.000000
-0.062601 0.027419 0.000000
-0.062258 0.026735 0.000000
-0.047275 0.023802 0.000000
-0.023588 0.016895 0.000000
-0.061832 0.026105 0.000000
-0.061332 0.025537 0.000000
-0.060763 0.025036 0.000000
-0.060133 0.024611 0.000000
-0.059449 0.024268 0.000000
-0.058719 0.024014 0.000000
-0.057948 0.023856 0.000000
-0.057145 0.023802 0.000000
-0.056951 0.023802 0.000000
-0.056414 0.023802 0.000000
-0.055603 0.023802 0.000000
-0.054586 0.023802 0.000000
-0.053432 0.023802 0.000000
-0.052210 0.023802 0.000000
-0.050988 0.023802 0.000000
-0.049834 0.023802 0.000000
-0.048817 0.023802 0.000000
-0.048006 0.023802 0.000000
-0.047469 0.023802 0.000000
-0.047275 -0.039350 0.000000
0.023786 -0.057111 0.000000
-0.023588 0.006875 0.000000
-0.023588 -0.002584 0.000000
-0.023588 -0.010919 0.000000
-0.023588 -0.017568 0.000000
-0.023588 -0.021970 0.000000
-0.023588 -0.023562 0.000000
0.015890 -0.023562 0.000000
0.015890 -0.047244 0.000000
0.057342 -0.023562 0.000000
0.058146 -0.023616 0.000000
0.058916 -0.023773 0.000000
0.059647 -0.024027 0.000000
0.060331 -0.024370 0.000000
0.060960 -0.024795 0.000000
0.061529 -0.025295 0.000000
0.062030 -0.025864 0.000000
0.062455 -0.026493 0.000000
0.062798 -0.027177 0.000000
0.063052 -0.027908 0.000000
0.063210 -0.028679 0.000000
0.063264 -0.029482 0.000000
0.063264 -0.029715 0.000000
0.063264 -0.030359 0.000000
0.063264 -0.031332 0.000000
0.063264 -0.032552 0.000000
0.063264 -0.033936 0.000000
0.063264 -0.035403 0.000000
0.063264 -0.036869 0.000000
0.063264 -0.038253 0.000000
0.063264 -0.039473 0.000000
-0.047203 -0.040421 0.000000
0.063264 -0.040446 0.000000
-0.046993 -0.041448 0.000000
0.047473 -0.047244 0.000000
0.063264 -0.041090 0.000000
0.063264 -0.041323 0.000000
0.063210 -0.042126 0.000000
-0.046655 -0.042422 0.000000
0.063052 -0.042897 0.000000
-0.046197 -0.043334 0.000000
0.062798 -0.043627 0.000000
-0.045630 -0.044173 0.000000
0.062455 -0.044311 0.000000
-0.044962 -0.044931 0.000000
0.062030 -0.044940 0.000000
-0.044204 -0.045599 0.000000
0.061529 -0.045509 0.000000
0.060960 -0.046010 0.000000
-0.043364 -0.046166 0.000000
0.060331 -0.046435 0.000000
-0.042453 -0.046623 0.000000
0.059647 -0.046778 0.000000
-0.041478 -0.046962 0.000000
0.058916 -0.047032 0.000000
-0.040451 -0.047172 0.000000
0.058146 -0.047190 0.000000
-0.039380 -0.047244 0.000000
0.057342 -0.047244 0.000000
-0.038292 -0.047244 0.000000
-0.035286 -0.047244 0.000000
-0.030744 -0.047244 0.000000
-0.025050 -0.047244 0.000000
-0.018590 -0.047244 0.000000
-0.011745 -0.047244 0.000000
-0.004900 -0.047244 0.000000
0.001561 -0.047244 0.000000
0.007254 -0.047244 0.000000
0.011796 -0.047244 0.000000
0.014803 -0.047244 0.000000
0.047473 -0.047438 0.000000
0.047473 -0.047975 0.000000
0.047473 -0.048785 0.000000
0.047473 -0.049802 0.000000
0.047473 -0.050955 0.000000
0.047473 -0.052177 0.000000
0.047473 -0.053399 0.000000
0.047473 -0.054553 0.000000
0.047473 -0.055569 0.000000
0.047473 -0.056380 0.000000
0.047473 -0.056917 0.000000
0.047473 -0.057111 0.000000
0.023840 -0.057914 0.000000
0.047419 -0.057914 0.000000
0.023997 -0.058685 0.000000
0.047261 -0.058685 0.000000
0.024251 -0.059415 0.000000
0.047007 -0.059415 0.000000
0.024594 -0.060099 0.000000
0.046664 -0.060099 0.000000
0.025020 -0.060728 0.000000
0.046238 -0.060728 0.000000
0.025521 -0.061297 0.000000
0.045738 -0.061297 0.000000
0.026089 -0.061798 0.000000
0.045169 -0.061798 0.000000
0.026719 -0.062223 0.000000
0.044539 -0.062223 0.000000
0.027403 -0.062566 0.000000
0.043855 -0.062566 0.000000
0.028134 -0.062820 0.000000
0.043125 -0.062820 0.000000
0.028904 -0.062978 0.000000
0.042354 -0.062978 0.000000
0.029707 -0.063032 0.000000
0.041551 -0.063032 0.000000
0.029940 -0.063032 0.000000
0.030585 -0.063032 0.000000
0.031558 -0.063032 0.000000
0.032778 -0.063032 0.000000
0.034162 -0.063032 0.000000
0.035629 -0.063032 0.000000
0.037096 -0.063032 0.000000
0.038480 -0.063032 0.000000
0.039700 -0.063032 0.000000
0.040674 -0.063032 0.000000
0.041318 -0.063032 0.000000
3 0 1 2
3 0 3 1
3 0 4 3
3 0 5 4
3 0 6 5
3 0 7 6
3 0 8 7
3 0 9 8
3 0 10 9
3 0 11 10
3 0 12 11
3 0 13 12
3 0 14 13
3 15 14 0
3 15 16 14
3 17 16 15
3 17 18 16
3 19 18 17
3 19 20 18
3 21 20 19
3 21 22 20
3 23 22 21
3 23 24 22
3 25 24 23
3 25 26 24
3 27 26 25
3 27 28 26
3 29 28 27
3 29 30 28
3 31 30 29
3 31 32 30
3 33 32 31
3 33 34 32
3 35 34 33
3 35 36 34
3 37 36 35
3 37 38 36
3 39 38 37
3 40 38 39
3 41 38 40
3 41 42 38
3 43 42 41
3 44 42 43
3 45 42 44
3 45 46 42
3 47 46 45
3 48 46 47
3 49 46 48
3 50 46 49
3 51 46 50
3 52 51 53
3 52 46 51
3 54 55 56
3 54 57 55
3 54 58 57
3 54 59 58
3 54 60 59
3 54 61 60
3 54 62 61
3 54 63 62
3 54 64 63
3 54 65 64
3 54 66 65
3 54 67 66
3 54 68 67
3 69 46 52
3 54 70 68
3 71 46 69
3 54 72 70
3 73 46 71
3 54 74 72
3 75 46 73
3 54 76 74
3 77 46 75
3 54 78 76
3 79 46 77
3 80 46 79
3 54 81 78
3 80 82 46
3 83 82 80
3 54 84 81
3 85 82 83
3 54 86 84
3 87 82 85
3 54 88 86
3 89 82 87
3 54 90 88
3 91 82 89
3 92 82 91
3 93 82 92
3 54 94 90
3 95 82 93
3 54 96 94
3 96 97 94
3 98 82 95
3 99 82 98
3 99 100 82
3 101 100 99
3 102 100 101
3 103 100 102
3 104 100 103
3 105 100 104
3 106 100 105
3 107 100 106
3 108 100 107
3 109 100 108
3 110 100 109
3 110 111 100
3 111 112 100
3 113 111 110
3 114 111 113
3 115 111 114
3 116 111 115
3 117 111 116
3 118 111 117
3 119 111 118
3 120 111 119
3 121 111 120
3 122 111 121
3 123 111 122
3 124 111 123
3 125 111 124
3 126 111 125
3 127 111 126
3 128 111 127
3 129 111 128
3 130 111 129
3 131 111 130
3 132 112 111
3 133 97 96
3 132 134 112
3 132 135 134
3 132 136 135
3 132 137 136
3 132 138 137
3 132 139 138
3 132 140 139
3 132 141 140
3 133 142 97
3 133 143 142
3 133 144 143
3 133 145 144
3 133 146 145
3 133 147 146
3 133 148 147
3 133 149 148
3 133 150 149
3 133 151 150
3 133 152 151
3 133 153 152
3 133 154 153
3 133 155 154
3 133 156 155
3 133 157 156
3 133 158 157
3 133 159 158
3 133 160 159
3 133 161 160
3 133 162 161
3 133 163 162
3 164 141 132
3 133 165 163
3 166 141 164
3 133 167 165
3 167 168 165
3 167 169 168
3 167 170 169
3 171 141 166
3 167 172 170
3 173 141 171
3 167 174 172
3 175 141 173
3 167 176 174
3 177 141 175
3 167 178 176
3 179 141 177
3 167 180 178
3 167 181 180
3 182 141 179
3 167 183 181
3 184 141 182
3 167 185 183
3 186 141 184
3 167 187 185
3 188 141 186
3 167 189 187
3 190 141 188
3 167 191 189
3 192 141 190
3 193 141 192
3 194 141 193
3 195 141 194
3 196 141 195
3 197 141 196
3 198 141 197
3 199 141 198
3 200 141 199
3 201 141 200
3 202 141 201
3 133 203 167
3 133 204 203
3 133 205 204
3 133 206 205
3 133 207 206
3 133 208 207
3 133 209 208
3 133 210 209
3 133 211 210
3 133 212 211
3 133 213 212
3 133 214 213
3 215 214 133
3 215 216 214
3 217 216 215
3 217 218 216
3 219 218 217
3 219 220 218
3 221 220 219
3 221 222 220
3 223 222 221
3 223 224 222
3 225 224 223
3 225 226 224
3 227 226 225
3 227 228 226
3 229 228 227
3 229 230 228
3 231 230 229
3 231 232 230
3 233 232 231
3 233 234 232
3 235 234 233
3 235 236 234
3 237 236 235
3 237 238 236
3 239 238 237
3 240 238 239
3 241 238 240
3 242 238 241
3 243 238 242
3 244 238 243
3 245 238 244
3 246 238 245
3 247 238 246
3 248 238 247
3 249 238 248

View File

@@ -25,13 +25,14 @@ void main()
vec4 textureColor = texture(iChannel0, texcoord.xy);
vec3 RGB = textureColor.rgb * vertexColor.rgb * color.rgb;
// alpha is a mix of texture alpha, vertex alpha, and uniform alpha affected by stippling
vec4 maskColor = texture(iChannel1, vertexUV);
float maskIntensity = (maskColor.r + maskColor.g + maskColor.b) / 3.0;
// read mask intensity as average of RGB
float maskIntensity = dot(texture(iChannel1, vertexUV).rgb, vec3(1.0/3.0));
// alpha is a mix of texture alpha, vertex alpha, and uniform alpha affected by stippling
float A = textureColor.a * vertexColor.a * color.a * maskIntensity;
A += stipple * ( int(gl_FragCoord.x + gl_FragCoord.y) % 2 );
// output RGBA
FragColor = vec4(RGB, clamp(A, 0.0, 1.0) );
// FragColor = texture(iChannel1, vertexUV);
}

View File

@@ -25,8 +25,7 @@ void main()
vec3 RGB = textureColor.rgb * vertexColor.rgb * color.rgb;
// alpha is a mix of texture alpha, vertex alpha, and uniform alpha affected by stippling
vec4 maskColor = texture(iChannel1, vertexUV);
float maskIntensity = (maskColor.r + maskColor.g + maskColor.b) / 3.0;
float maskIntensity = dot(texture(iChannel1, vertexUV).rgb, vec3(1.0/3.0));
float A = textureColor.a * vertexColor.a * color.a * maskIntensity;
A -= stipple * ( int(gl_FragCoord.x + gl_FragCoord.y) % 2 > 0 ? 0.05 : 0.95 );

View File

@@ -12,8 +12,8 @@ uniform vec4 color; // drawing color
uniform vec4 uv;
// Mask Shader
uniform vec2 size;
uniform float blur;
uniform vec2 size; // size of the mask area
uniform float blur; // percent of blur
float sdRoundBox( in vec2 p, in vec2 b, in float r )
{
@@ -29,7 +29,7 @@ void main()
float d = sdRoundBox( uv, vec2(size.x * iResolution.x/iResolution.y, size.y), blur * 0.5 );
vec3 col = vec3(1.0 - sign(d));
col *= 1.0 - exp( -60.0/ (blur * 100.f + 1.0) * abs(d));
col *= 1.0 - exp( -600.0/ (blur * 1000.f + 1.0) * abs(d));
FragColor = vec4( col, 1.0 );
}

100
rsc/shaders/mask_draw.fs Normal file
View File

@@ -0,0 +1,100 @@
#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
in vec2 vertexUV;
// from General Shader
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform mat4 iTransform; // image transformation
uniform vec4 color; // drawing color
uniform vec4 uv;
// Mask Shader
uniform sampler2D iChannel0; // base texture
uniform vec2 size; // size of the mask area
uniform float blur; // percent of blur
uniform vec4 cursor;
uniform vec3 brush;
uniform int option;
float sdBox( in vec2 p, in float b)
{
vec2 q = abs(p) - vec2(b);
float d = min( max(q.x, q.y), 0.0) + length(max(q,0.0));
return 1.0 - abs(d) * step(d, 0.0);
}
float sdCircle( in vec2 p, in float b)
{
return ( length( p ) / b );
}
const mat3 KERNEL = mat3( 0.0625, 0.125, 0.0625,
0.125, 0.25, 0.125,
0.0625, 0.125, 0.0625);
vec3 gaussian()
{
vec2 filter_step = 1.f / textureSize(iChannel0, 0);
int i = 0, j = 0;
vec3 sum = vec3(0.0);
for (i = 0; i<3; ++i)
for (j = 0; j<3; ++j)
sum += texture(iChannel0, vertexUV.xy + filter_step * vec2 (i-1, j-1) ).rgb * KERNEL[i][j];
return sum;
}
void main()
{
// blur the image incrementally each step
vec3 color = gaussian();
// vec3 color = texture(iChannel0, vertexUV).rgb; // raw color
if ( option > 0 ) {
// fragment coordinates
vec2 uv = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy;
// adjust coordinates to match scaling area
uv.x *= cursor.z ;
uv.y *= cursor.w ;
// cursor coordinates
vec2 cursor = vec2(cursor.x, - cursor.y);
// use distance function relative to length brush.x (size), depending on brush.z (shape):
// - brush.z = 0 : circle shape
// - brush.z = 1 : square shape
float d = (1.0 -brush.z) * sdCircle(cursor-uv, brush.x) + brush.z * sdBox(uv-cursor, brush.x);
// modify only the pixels inside the brush
if( d < 1.0 )
{
// mask intensity
float c = dot(color, vec3(1.0/3.0));
// depending on option:
// - option 1 : paint (add color)
// - option 2 : erase (substract color)
float p = c + (option > 1 ? d - 1.0 : 1.0 - d );
// new intensity is a mix betwen current intensity and painted intensity
c = (1.0 - brush.y) * c + (brush.y) * p;
// distribute value in RGB (will be averaged in image.fs)
// NB : this encodes the mask intensity over 3*8 bits
c *= 3.0;
color.r = clamp(c, 0.0, 1.0);
color.g = step(1.0, c) * (clamp(c, 1.0, 2.0) - 1.0);
color.b = step(2.0, c) * (clamp(c, 2.0, 3.0) - 2.0);
}
}
FragColor = vec4( clamp(color, 0.0, 1.0), 1.0);
}

View File

@@ -12,8 +12,8 @@ uniform vec4 color; // drawing color
uniform vec4 uv;
// Mask Shader
uniform vec2 size;
uniform float blur;
uniform vec2 size; // size of the mask area
uniform float blur; // percent of blur
// See: http://www.iquilezles.org/www/articles/ellipsoids/ellipsoids.htm
float sdEllipsoid( in vec2 p, in vec2 r )
@@ -57,7 +57,7 @@ void main()
float d = sdEllipse( uv, vec2(size.x * iResolution.x/iResolution.y, size.y) );
vec3 col = vec3(1.0- sign(d));
col *= 1.0 - exp( -60.0/ (blur * 100.f + 1.0) * abs(d));
col *= 1.0 - exp( -600.0/ (blur * 1000.f + 1.0) * abs(d));
FragColor = vec4( col, 1.0 );
}

View File

@@ -0,0 +1,32 @@
#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
in vec2 vertexUV;
// from General Shader
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform mat4 iTransform; // image transformation
uniform vec4 color; // drawing color
uniform vec4 uv;
// Mask Shader
uniform vec2 size; // size of the mask area
uniform float blur; // percent of blur
float sdHorizontal( in vec2 p, in float y )
{
float d = step(0.0, y);
d = d * smoothstep(0.0, 2.0 * blur, y + p.y) + (1.0-d)*smoothstep(-2.0 * blur, 0.0, (-2.0 * blur) - (y + p.y));
return d;
}
void main()
{
vec2 uv = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy;
float d = sdHorizontal( uv, size.y);
FragColor = vec4( vec3(d), 1.0 );
}

View File

@@ -12,8 +12,8 @@ uniform vec4 color; // drawing color
uniform vec4 uv;
// Mask Shader
uniform vec2 size;
uniform float blur;
uniform vec2 size; // size of the mask area
uniform float blur; // percent of blur
float sdRoundSide( in vec2 p, in vec2 b, in float r )
{
@@ -21,16 +21,47 @@ float sdRoundSide( in vec2 p, in vec2 b, in float r )
return min(max(q.x,q.y),0.0) + length(max(q,0.0)) - r;
}
float sdHorizontal( in vec2 p, in float y )
{
float d = 0.0;
// if (y < 0.0) {
// d = smoothstep(-2.0 * blur, 0.0, (-2.0 * blur) - (y + p.y));
//// d = smoothstep(-2.0 * blur, 0.0, -y -blur -p.y); // centered blur
// }
// else {
// d = smoothstep(0.0, 2.0 * blur, y + p.y);
//// d = smoothstep(0.0, 2.0 * blur, y +blur +p.y); // centered blur
// }
d = step(0.0, y);
d = d * smoothstep(0.0, 2.0 * blur, y + p.y) + (1.0-d)*smoothstep(-2.0 * blur, 0.0, (-2.0 * blur) - (y + p.y));
return d;
}
void main()
{
vec2 uv = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy;
uv.x *= iResolution.x / iResolution.y;
float d = sdRoundSide( uv, vec2(size.x * iResolution.x/iResolution.y, size.y), blur );
float d = sdHorizontal( uv, size.y);
vec3 col = vec3(1.0 - sign(d));
float delta = 0.5 * length(min(size,1.0));
col *= 1.0 - exp( -30.0/ (blur * 100.0 * delta + 1.0) * abs(d));
vec3 col = vec3(d);
// col = vec3(sign(d));
// col *= 1.0 - exp( -600.0/ (blur * 1000.0 + 1.0) * abs(d));
// float d = sdRoundSide( uv, vec2(size.x * iResolution.x/iResolution.y, size.y), blur );
// vec3 col = vec3(1.0 - sign(d));
// float delta = 0.5 * length(min(size,1.0));
// col *= 1.0 - exp( -300.0/ (blur * 1000.0 * delta + 1.0) * abs(d));
FragColor = vec4( col, 1.0 );
}

View File

@@ -12,9 +12,8 @@ uniform vec4 color; // drawing color
uniform vec4 uv;
// Mask Shader
uniform vec2 size;
uniform float blur;
uniform float invert;
uniform vec2 size; // size of the mask area
uniform float blur; // percent of blur
float udSegment( in vec2 p, in vec2 a, in vec2 b )
{
@@ -49,7 +48,7 @@ void main()
float d = udSegment( uv, v1, v2 )- th;
vec3 col = vec3(1.0- sign(d));
col *= 1.0 - exp( -60.0/ (blur * 100.0 + 1.0) * abs(d));
col *= 1.0 - exp( -600.0/ (blur * 1000.0 + 1.0) * abs(d));
FragColor = vec4( col, 1.0 );
}

View File

@@ -12,9 +12,8 @@ uniform vec4 color; // drawing color
uniform vec4 uv;
// Mask Shader
uniform vec2 size;
uniform float blur;
uniform vec2 size; // size of the mask area
uniform float blur; // percent of blur
float sdRoundSide( in vec2 p, in vec2 b, in float r )
{
@@ -31,8 +30,8 @@ void main()
float d = sdRoundSide( uv, vec2(s.x * iResolution.x/iResolution.y, s.y), blur );
vec3 col = vec3(1.0 - sign(d));
float delta = 0.5 * length(min(size,1.0));
col *= 1.0 - exp( -20.0/ (blur * 100.0 * delta + 1.0) * abs(d) );
float delta = 0.5 * length(min(size, 1.0));
col *= 1.0 - exp( -200.0/ (blur * 1000.0 * delta + 1.0) * abs(d) );
FragColor = vec4( col, 1.0 );
}

View File

@@ -0,0 +1,32 @@
#version 330 core
out vec4 FragColor;
in vec4 vertexColor;
in vec2 vertexUV;
// from General Shader
uniform vec3 iResolution; // viewport resolution (in pixels)
uniform mat4 iTransform; // image transformation
uniform vec4 color; // drawing color
uniform vec4 uv;
// Mask Shader
uniform vec2 size; // size of the mask area
uniform float blur; // percent of blur
float sdVertical( in vec2 p, in float x )
{
float d = step(0.0, x);
d = d * smoothstep(0.0, 2.0 * blur, x + p.x) + (1.0-d)*smoothstep(-2.0 * blur, 0.0, (-2.0 * blur) - (x + p.x));
return d;
}
void main()
{
vec2 uv = -1.0 + 2.0 * gl_FragCoord.xy / iResolution.xy;
float d = sdVertical( uv, -size.x);
FragColor = vec4( vec3(d), 1.0 );
}