diff --git a/data/layout.xml b/data/layout.xml
index 54e87ab..e8f59e5 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -305,6 +305,9 @@
+
+
+
@@ -347,6 +350,10 @@
+
+
+
+
diff --git a/src/app_shaders.cpp b/src/app_shaders.cpp
index e272a49..20482b0 100644
--- a/src/app_shaders.cpp
+++ b/src/app_shaders.cpp
@@ -253,6 +253,7 @@ void App::initShaders()
"uniform mediump float pattern_bright;\n"
"uniform mediump float pattern_contr;\n"
"uniform mediump vec2 pattern_offset;\n"
+ "uniform mediump bool pattern_invert;\n"
"in mediump vec2 uv;\n"
"out mediump vec4 frag;\n"
@@ -266,6 +267,8 @@ void App::initShaders()
" if (use_pattern){\n"
" mediump vec2 rscale = resolution / vec2(512.0);\n"
" mediump float patt = texture(tex_pattern, uv * (0.5 / pattern_scale) * rscale + pattern_offset).r;\n"
+ " if (pattern_invert)\n"
+ " patt = 1.0 - patt;\n"
" if (pattern_bright != 0.5)\n"
" patt = brightness1(patt, 1.0 - pattern_bright);\n"
" if (pattern_contr != 0.5)\n"
@@ -433,6 +436,7 @@ void App::initShaders()
"uniform mediump float pattern_bright;\n"
"uniform mediump float pattern_contr;\n"
"uniform mediump vec2 pattern_offset;\n"
+ "uniform mediump bool pattern_invert;\n"
"in mediump vec2 uv;\n"
"in mediump vec2 uv_2;\n"
@@ -451,6 +455,8 @@ void App::initShaders()
" if (use_pattern){\n"
" mediump vec2 rscale = resolution / vec2(512.0);\n"
" mediump float patt = texture(tex_pattern, uv2 * (0.5 / pattern_scale) * rscale + pattern_offset).r;\n"
+ " if (pattern_invert)\n"
+ " patt = 1.0 - patt;\n"
" if (pattern_bright != 0.5)\n"
" patt = brightness1(patt, 1.0 - pattern_bright);\n"
" if (pattern_contr != 0.5)\n"
diff --git a/src/app_vr.cpp b/src/app_vr.cpp
index f0ecba2..4ef6408 100644
--- a/src/app_vr.cpp
+++ b/src/app_vr.cpp
@@ -88,6 +88,11 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat
{
const auto& b = canvas->m_canvas->m_current_stroke->m_brush;
sampler.bind(0);
+
+ glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
+ if (b->m_pattern_flipx) patt_scale.x *= -1.f;
+ if (b->m_pattern_flipy) patt_scale.y *= -1.f;
+
ShaderManager::use(kShader::CompDraw);
ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_int(kShaderUniform::TexStroke, 1);
@@ -103,7 +108,8 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat
ShaderManager::u_int(kShaderUniform::UseDual, false);
ShaderManager::u_int(kShaderUniform::UsePattern, b->m_pattern_enabled && !b->m_pattern_eachsample);
ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z);
- ShaderManager::u_vec2(kShaderUniform::PatternScale, glm::vec2(b->m_pattern_scale));
+ ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale);
+ ShaderManager::u_float(kShaderUniform::PatternInvert, b->m_pattern_invert);
ShaderManager::u_float(kShaderUniform::PatternBright, b->m_pattern_brightness);
ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast);
ShaderManager::u_vec2(kShaderUniform::PatternOffset, Canvas::I->m_pattern_offset);
diff --git a/src/brush.h b/src/brush.h
index b10099b..1fe1ef3 100644
--- a/src/brush.h
+++ b/src/brush.h
@@ -77,6 +77,7 @@ public:
float m_pattern_scale = .25f;
float m_pattern_brightness = 0.5f;
float m_pattern_contrast = 0.5f;
+ bool m_pattern_rand_offset = false;
bool load_tip(const std::string& path, const std::string& thumb);
bool load_dual(const std::string& path, const std::string& thumb);
diff --git a/src/canvas.cpp b/src/canvas.cpp
index 80b6d70..244ebdb 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -483,6 +483,10 @@ void Canvas::stroke_draw()
m_sampler.bind(3);
//m_sampler_linear.bind(5);
+ glm::vec2 patt_scale = glm::vec2(brush->m_pattern_scale);
+ if (brush->m_pattern_flipx) patt_scale.x *= -1.f;
+ if (brush->m_pattern_flipy) patt_scale.y *= -1.f;
+
glDisable(GL_BLEND);
ShaderManager::use(kShader::Stroke);
ShaderManager::u_int(kShaderUniform::Tex, 0); // brush
@@ -493,7 +497,8 @@ void Canvas::stroke_draw()
//ShaderManager::u_int(kShaderUniform::TexMixA, 4); // mixer
ShaderManager::u_vec2(kShaderUniform::Resolution, { m_width, m_height });
ShaderManager::u_float(kShaderUniform::PatternAlpha, brush->m_pattern_opacity);
- ShaderManager::u_vec2(kShaderUniform::PatternScale, glm::vec2(brush->m_pattern_scale));
+ ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale);
+ ShaderManager::u_float(kShaderUniform::PatternInvert, brush->m_pattern_invert);
ShaderManager::u_float(kShaderUniform::PatternBright, brush->m_pattern_brightness);
ShaderManager::u_float(kShaderUniform::PatternContrast, brush->m_pattern_contrast);
ShaderManager::u_vec2(kShaderUniform::PatternOffset, m_pattern_offset);
@@ -782,6 +787,10 @@ void Canvas::stroke_commit()
}
else
{
+ glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
+ if (b->m_pattern_flipx) patt_scale.x *= -1.f;
+ if (b->m_pattern_flipy) patt_scale.y *= -1.f;
+
ShaderManager::use(kShader::CompDraw);
ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_int(kShaderUniform::TexStroke, 1);
@@ -798,7 +807,8 @@ void Canvas::stroke_commit()
ShaderManager::u_int(kShaderUniform::UseDual, b->m_dual_enabled);
ShaderManager::u_int(kShaderUniform::UsePattern, b->m_pattern_enabled && !b->m_pattern_eachsample);
ShaderManager::u_int(kShaderUniform::DualBlendMode, b->m_dual_blend_mode);
- ShaderManager::u_vec2(kShaderUniform::PatternScale, glm::vec2(b->m_pattern_scale));
+ ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale);
+ ShaderManager::u_float(kShaderUniform::PatternInvert, b->m_pattern_invert);
ShaderManager::u_float(kShaderUniform::PatternBright, b->m_pattern_brightness);
ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast);
ShaderManager::u_vec2(kShaderUniform::PatternOffset, m_pattern_offset);
@@ -876,7 +886,9 @@ void Canvas::stroke_start(glm::vec3 point, float pressure)
m_commit_delayed = false;
}
- m_pattern_offset = glm::vec2((rand()%1000)*0.001f, (rand()%1000)*0.001f);
+ m_pattern_offset = m_current_brush->m_pattern_rand_offset ?
+ glm::vec2((rand()%1000)*0.001f, (rand()%1000)*0.001f) :
+ glm::vec2(0);
m_current_stroke = std::make_unique();
m_current_stroke->m_camera.rot = m_cam_rot;
diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp
index 5aad5f3..18e6af5 100644
--- a/src/node_canvas.cpp
+++ b/src/node_canvas.cpp
@@ -203,6 +203,11 @@ void NodeCanvas::draw()
else if(m_canvas->m_current_stroke && m_canvas->m_show_tmp && m_canvas->m_current_layer_idx == layer_index)
{
m_sampler.bind(0);
+
+ glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
+ if (b->m_pattern_flipx) patt_scale.x *= -1.f;
+ if (b->m_pattern_flipy) patt_scale.y *= -1.f;
+
ShaderManager::use(kShader::CompDraw);
ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_int(kShaderUniform::TexStroke, 1);
@@ -220,7 +225,8 @@ void NodeCanvas::draw()
ShaderManager::u_int(kShaderUniform::UseDual, b->m_dual_enabled);
ShaderManager::u_int(kShaderUniform::DualBlendMode, b->m_dual_blend_mode);
ShaderManager::u_int(kShaderUniform::UsePattern, b->m_pattern_enabled && !b->m_pattern_eachsample);
- ShaderManager::u_vec2(kShaderUniform::PatternScale, glm::vec2(b->m_pattern_scale));
+ ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale);
+ ShaderManager::u_float(kShaderUniform::PatternInvert, b->m_pattern_invert);
ShaderManager::u_float(kShaderUniform::PatternBright, b->m_pattern_brightness);
ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast);
ShaderManager::u_vec2(kShaderUniform::PatternOffset, Canvas::I->m_pattern_offset);
diff --git a/src/node_panel_brush.cpp b/src/node_panel_brush.cpp
index 812e104..ceb4d4b 100644
--- a/src/node_panel_brush.cpp
+++ b/src/node_panel_brush.cpp
@@ -601,6 +601,7 @@ bool NodePanelBrushPreset::save()
i.m_pattern_scale = b->m_brush->m_pattern_scale;
i.m_pattern_brightness = b->m_brush->m_pattern_brightness;
i.m_pattern_contrast = b->m_brush->m_pattern_contrast;
+ i.m_pattern_rand_offset = b->m_brush->m_pattern_rand_offset;
fwrite(&i, sizeof(i), 1, fp);
fwrite(b->m_brush->m_name.c_str(), 1, b->m_brush->m_name.size(), fp);
@@ -650,7 +651,7 @@ bool NodePanelBrushPreset::restore()
b->m_tip_angle = i.m_tip_angle;
b->m_tip_angle_delay = i.m_tip_angle_delay;
b->m_tip_mix = i.m_tip_mix;
- b->m_pattern_opacity = i.m_pattern_opacity;
+ b->m_pattern_opacity = i.m_pattern_opacity;
b->m_tip_wet = i.m_tip_wet;
b->m_tip_noise = i.m_tip_noise;
b->m_tip_hue = i.m_tip_hue;
@@ -694,6 +695,7 @@ bool NodePanelBrushPreset::restore()
b->m_pattern_scale = i.m_pattern_scale;
b->m_pattern_brightness = i.m_pattern_brightness;
b->m_pattern_contrast = i.m_pattern_contrast;
+ b->m_pattern_rand_offset = i.m_pattern_rand_offset;
b->m_name.resize(i.m_name_len);
b->m_brush_path.resize(i.m_brush_path_len);
diff --git a/src/node_panel_brush.h b/src/node_panel_brush.h
index 85e9184..06a5a85 100644
--- a/src/node_panel_brush.h
+++ b/src/node_panel_brush.h
@@ -157,6 +157,7 @@ class NodePanelBrushPreset : public Node
float m_pattern_scale = .25f;
float m_pattern_brightness = 0.5f;
float m_pattern_contrast = 0.5f;
+ bool m_pattern_rand_offset = false;
};
public:
std::function& brush)> on_brush_changed;
diff --git a/src/node_panel_stroke.cpp b/src/node_panel_stroke.cpp
index fb07465..c3ed845 100644
--- a/src/node_panel_stroke.cpp
+++ b/src/node_panel_stroke.cpp
@@ -70,6 +70,7 @@ void NodePanelStroke::update_controls()
m_pattern_invert->checked = b->m_pattern_invert;
m_pattern_flipx->checked = b->m_pattern_flipx;
m_pattern_flipy->checked = b->m_pattern_flipy;
+ m_pattern_rand_offset->checked = b->m_pattern_rand_offset;
m_pattern_scale->m_value.x = b->m_pattern_scale;
m_pattern_brightness->m_value.x = b->m_pattern_brightness;
m_pattern_contrast->m_value.x = b->m_pattern_contrast;
@@ -342,6 +343,7 @@ void NodePanelStroke::init_controls()
init_checkbox(m_pattern_invert, "pattern-invert", &Brush::m_pattern_invert);
init_checkbox(m_pattern_flipx, "pattern-flipx", &Brush::m_pattern_flipx);
init_checkbox(m_pattern_flipy, "pattern-flipy", &Brush::m_pattern_flipy);
+ init_checkbox(m_pattern_rand_offset, "pattern-rand-offset", &Brush::m_pattern_rand_offset);
init_slider(m_dual_size, "dual-size", &Brush::m_dual_size);
init_slider(m_dual_spacing, "dual-spacing", &Brush::m_dual_spacing);
diff --git a/src/node_panel_stroke.h b/src/node_panel_stroke.h
index b1652d2..528fbbf 100644
--- a/src/node_panel_stroke.h
+++ b/src/node_panel_stroke.h
@@ -74,6 +74,7 @@ public:
NodeCheckBox* m_pattern_invert;
NodeCheckBox* m_pattern_flipx;
NodeCheckBox* m_pattern_flipy;
+ NodeCheckBox* m_pattern_rand_offset;
NodeSliderH* m_pattern_scale;
NodeSliderH* m_pattern_brightness;
NodeSliderH* m_pattern_contrast;
diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp
index ea6033e..ed982dc 100644
--- a/src/node_stroke_preview.cpp
+++ b/src/node_stroke_preview.cpp
@@ -70,6 +70,10 @@ void NodeStrokePreview::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2
glScissor(bb_min.x, bb_min.y, bb_sz.x, bb_sz.y);
const auto& b = m_brush;
+ glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
+ if (b->m_pattern_flipx) patt_scale.x *= -1.f;
+ if (b->m_pattern_flipy) patt_scale.y *= -1.f;
+
ShaderManager::use(kShader::CompDraw);
ShaderManager::u_int(kShaderUniform::Tex, 0);
ShaderManager::u_int(kShaderUniform::TexStroke, 1);
@@ -87,10 +91,11 @@ void NodeStrokePreview::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2
ShaderManager::u_int(kShaderUniform::UseDual, b->m_dual_enabled);
ShaderManager::u_int(kShaderUniform::UsePattern, b->m_pattern_enabled && !b->m_pattern_eachsample);
ShaderManager::u_int(kShaderUniform::DualBlendMode, b->m_dual_blend_mode);
- ShaderManager::u_vec2(kShaderUniform::PatternScale, glm::vec2(b->m_pattern_scale));
+ ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale);
+ ShaderManager::u_float(kShaderUniform::PatternInvert, b->m_pattern_invert);
ShaderManager::u_float(kShaderUniform::PatternBright, b->m_pattern_brightness);
ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast);
- ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(0));
+ ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(b->m_pattern_rand_offset ? 0.5f: 0.0f));
ShaderManager::u_float(kShaderUniform::DualAlpha, b->m_dual_opacity);
m_sampler_linear.bind(0);
@@ -279,6 +284,10 @@ void NodeStrokePreview::draw_stroke()
}
}
+ glm::vec2 patt_scale = glm::vec2(b->m_pattern_scale);
+ if (b->m_pattern_flipx) patt_scale.x *= -1.f;
+ if (b->m_pattern_flipy) patt_scale.y *= -1.f;
+
glDisable(GL_BLEND);
ShaderManager::use(kShader::Stroke);
ShaderManager::u_int(kShaderUniform::Tex, 0); // brush
@@ -289,10 +298,11 @@ void NodeStrokePreview::draw_stroke()
//ShaderManager::u_int(kShaderUniform::TexMixA, 4); // mixer
ShaderManager::u_vec2(kShaderUniform::Resolution, size);
ShaderManager::u_float(kShaderUniform::PatternAlpha, b->m_pattern_opacity);
- ShaderManager::u_vec2(kShaderUniform::PatternScale, glm::vec2(b->m_pattern_scale));
+ ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale);
+ ShaderManager::u_float(kShaderUniform::PatternInvert, b->m_pattern_invert);
ShaderManager::u_float(kShaderUniform::PatternBright, b->m_pattern_brightness);
ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast);
- ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(0));
+ ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(b->m_pattern_rand_offset ? 0.5f : 0.0f));
ShaderManager::u_int(kShaderUniform::UsePattern, b->m_pattern_enabled && b->m_pattern_eachsample);
ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj);
@@ -391,10 +401,11 @@ void NodeStrokePreview::draw_stroke()
ShaderManager::u_int(kShaderUniform::UseDual, b->m_dual_enabled);
ShaderManager::u_int(kShaderUniform::UsePattern, b->m_pattern_enabled && !b->m_pattern_eachsample);
ShaderManager::u_int(kShaderUniform::DualBlendMode, b->m_dual_blend_mode);
- ShaderManager::u_vec2(kShaderUniform::PatternScale, glm::vec2(b->m_pattern_scale));
+ ShaderManager::u_vec2(kShaderUniform::PatternScale, patt_scale);
+ ShaderManager::u_float(kShaderUniform::PatternInvert, b->m_pattern_invert);
ShaderManager::u_float(kShaderUniform::PatternBright, b->m_pattern_brightness);
ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast);
- ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(0));
+ ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(b->m_pattern_rand_offset ? 0.5f : 0.0f));
ShaderManager::u_float(kShaderUniform::DualAlpha, b->m_dual_opacity);
m_sampler_linear.bind(0);
diff --git a/src/shader.h b/src/shader.h
index d2e0c1c..95eeba8 100644
--- a/src/shader.h
+++ b/src/shader.h
@@ -36,6 +36,7 @@ enum class kShaderUniform : uint16_t
LightDir = const_hash("light_dir"),
Mode = const_hash("mode"),
Ambient = const_hash("ambient"),
+ PatternInvert = const_hash("pattern_invert"),
PatternScale = const_hash("pattern_scale"),
PatternBright = const_hash("pattern_bright"),
PatternContrast = const_hash("pattern_contr"),