diff --git a/data/layout.xml b/data/layout.xml
index 1ff969b..1ccd02b 100644
--- a/data/layout.xml
+++ b/data/layout.xml
@@ -583,6 +583,9 @@
+
+
+
@@ -626,6 +629,10 @@
+
+
+
+
diff --git a/src/abr.cpp b/src/abr.cpp
index dbc0d5a..b066a9b 100644
--- a/src/abr.cpp
+++ b/src/abr.cpp
@@ -225,6 +225,15 @@ std::vector> ABR::compute_brushes(const std::string& path
}
+ // Color Dynamics
+ if (p->value("useColorDynamics"))
+ {
+ b->m_jitter_sat = p->value("Strt") * 0.01f;
+ b->m_jitter_hue = p->value("H ") * 0.01f;
+ b->m_jitter_val = p->value("Brgh") * 0.01f;
+ b->m_jitter_hsv_eachsample = p->value("colorDynamicsPerTip");
+ }
+
std::vector modes = {
"normal", // normal (not in Photoshop)
"Mltp", // multiply
diff --git a/src/app_layout.cpp b/src/app_layout.cpp
index aa5e93c..a08c3d6 100644
--- a/src/app_layout.cpp
+++ b/src/app_layout.cpp
@@ -150,7 +150,9 @@ void App::init_sidebar()
stroke->set_size(value, true, true);
};
quick->on_brush_change = [this](Node*, std::shared_ptr b) {
+ auto c = Canvas::I->m_current_brush->m_tip_color;
*Canvas::I->m_current_brush = *b;
+ Canvas::I->m_current_brush->m_tip_color = c;
brush_update();
};
diff --git a/src/brush.cpp b/src/brush.cpp
index be52ec8..495be16 100644
--- a/src/brush.cpp
+++ b/src/brush.cpp
@@ -129,12 +129,6 @@ bool BrushMesh::create()
}
StrokeSample Stroke::randomize_sample(const glm::vec3& pos, float pressure, float dir_angle)
{
- auto rnd_nor = [&] { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1]
- auto rnd_neg = [&] { return float((double)prng() / (double)prng.max() * 2.0 - 1.0); }; // normalized [-1, +1]
- auto rnd_rad = [&] { return float((double)prng() / (double)prng.max() * M_PI * 2.0); }; // normalized [0, 2pi]
- auto rnd_vec = [&] { float rad = rnd_rad(); return glm::vec3(cosf(rad), sinf(rad), 0); }; // normalized direction vector
- auto rnd_bneg = [&] { return prng() % 2 == 0 ? -1.f : 1.f; }; // -1 or 1
-
float size_dyn = m_brush->m_tip_size_pressure ? pressure : 1.f;
float flow_dyn = m_brush->m_tip_flow_pressure ? pressure : 1.f;
float opacity_dyn = m_brush->m_tip_opacity_pressure ? pressure : 1.f;
@@ -162,14 +156,24 @@ StrokeSample Stroke::randomize_sample(const glm::vec3& pos, float pressure, floa
s.pos = pos + (scatter_scale * rnd_vec() * m_brush->m_jitter_scatter * s.size * 0.5f); // 0.5 because PS scatters by half size
s.flow = m_brush->m_tip_flow * (1.f - rnd_nor() * m_brush->m_jitter_flow) * flow_dyn;
s.opacity = m_brush->m_tip_opacity * (1.f - rnd_nor() * m_brush->m_jitter_opacity) * opacity_dyn;
- auto hsv = convert_rgb2hsv(m_brush->m_tip_color);
- hsv.x = glm::clamp(glm::mix(hsv.x, (pressure - 0.5f) * 2.0f, m_brush->m_tip_hue) + (rnd_nor() - 0.5f) * m_brush->m_jitter_hue, 0.f, 1.f);
- hsv.y = glm::clamp(glm::mix(hsv.y, (1.f - pressure - 0.5f) * 2.0f, m_brush->m_tip_sat) + (rnd_nor() - 0.5f) * m_brush->m_jitter_sat, 0.f, 1.f);
- hsv.z = glm::clamp(glm::mix(hsv.z, (pressure - 0.5f) * 2.0f, m_brush->m_tip_val) + (rnd_nor() - 0.5f) * m_brush->m_jitter_val, 0.f, 1.f);
+
+ auto hsv = m_tip_color;
+ float eachtip = m_brush->m_jitter_hsv_eachsample ? 1.f : 0.f;
+ hsv.x = glm::fract(glm::mix(hsv.x, (pressure - 0.5f) * 2.0f, m_brush->m_tip_hue) + (rnd_nor() - 0.5f) * m_brush->m_jitter_hue * eachtip);
+ hsv.y = glm::clamp(glm::mix(hsv.y, (1.f - pressure - 0.5f) * 2.0f, m_brush->m_tip_sat) + (rnd_nor() - 0.5f) * m_brush->m_jitter_sat * eachtip, 0.f, 1.f);
+ hsv.z = glm::clamp(glm::mix(hsv.z, (pressure - 0.5f) * 2.0f, m_brush->m_tip_val) + (rnd_nor() - 0.5f) * m_brush->m_jitter_val * eachtip, 0.f, 1.f);
m_hsv_jitter.add(hsv);
s.col = convert_hsv2rgb(m_hsv_jitter.average());
+
return s;
}
+
+void Stroke::randomize_prng()
+{
+ std::random_device rd;
+ prng.seed(rd());
+}
+
std::vector Stroke::compute_samples()
{
if (m_keypoints.empty()) return {};
@@ -320,6 +324,12 @@ void Stroke::start(const std::shared_ptr& brush)
float raw_size = glm::clamp(m_brush->m_tip_size / glm::tan(glm::radians(m_camera.fov * 0.5f)), 1.f, m_max_size);
float size = aspect_width * glm::min(m_brush->m_tip_scale.x, m_brush->m_tip_scale.y) * raw_size;
m_step = glm::max(0.5f, m_brush->m_tip_spacing * size);
+
+ auto hsv = convert_rgb2hsv(m_brush->m_tip_color);
+ hsv.x = glm::fract(hsv.x + (rnd_nor() - 0.5f) * m_brush->m_jitter_hue);
+ hsv.y = glm::clamp(hsv.y + (rnd_nor() - 0.5f) * m_brush->m_jitter_sat, 0.f, 1.f);
+ hsv.z = glm::clamp(hsv.z + (rnd_nor() - 0.5f) * m_brush->m_jitter_val, 0.f, 1.f);
+ m_tip_color = hsv;
m_direction.resize(std::max(1, m_brush->m_tip_angle_smooth * 200.f / m_step));
prng.seed(0);
diff --git a/src/brush.h b/src/brush.h
index 6c895f9..d45f77c 100644
--- a/src/brush.h
+++ b/src/brush.h
@@ -54,6 +54,7 @@ public:
float m_jitter_hue = 0;
float m_jitter_sat = 0;
float m_jitter_val = 0;
+ bool m_jitter_hsv_eachsample = false;
float m_jitter_aspect = 0;
bool m_jitter_aspect_bothaxis = false;
int m_blend_mode = 0;
@@ -172,6 +173,7 @@ public:
float m_step = 0;
float m_max_size = FLT_MAX;
bool m_filter_points = true;
+ glm::vec3 m_tip_color;
Camera m_camera;
std::shared_ptr m_brush;
cbuffer m_direction{ 1 };
@@ -182,11 +184,18 @@ public:
std::vector> m_hold_points;
std::vector m_samples;
int m_last_kp;
- std::minstd_rand prng;
+ std::mt19937 prng;
void start(const std::shared_ptr& brush);
void add_point(glm::vec3 pos, float pressure);
void reset(bool clear_keypoints = false);
bool has_sample();
std::vector compute_samples();
StrokeSample randomize_sample(const glm::vec3& pos, float pressure, float curve_angle);
+
+ void randomize_prng();
+ float rnd_nor() { return float((double)prng() / (double)prng.max()); }; // normalized [0, +1]
+ float rnd_neg() { return float((double)prng() / (double)prng.max() * 2.0 - 1.0); }; // normalized [-1, +1]
+ float rnd_rad() { return float((double)prng() / (double)prng.max() * M_PI * 2.0); }; // normalized [0, 2pi]
+ glm::vec3 rnd_vec() { float rad = rnd_rad(); return glm::vec3(cosf(rad), sinf(rad), 0); }; // normalized direction vector
+ float rnd_bneg() { return prng() % 2 == 0 ? -1.f : 1.f; }; // -1 or 1
};
diff --git a/src/canvas.cpp b/src/canvas.cpp
index 2f69360..a4b6749 100644
--- a/src/canvas.cpp
+++ b/src/canvas.cpp
@@ -900,6 +900,7 @@ void Canvas::stroke_start(glm::vec3 point, float pressure)
m_current_stroke = std::make_unique();
m_current_stroke->m_camera.rot = m_cam_rot;
m_current_stroke->m_camera.fov = m_cam_fov;
+ m_current_stroke->randomize_prng();
m_current_stroke->start(m_current_brush);
m_current_stroke->add_point(point, pressure);
diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp
index aa3abb3..bbc1c85 100644
--- a/src/canvas_modes.cpp
+++ b/src/canvas_modes.cpp
@@ -239,7 +239,7 @@ void CanvasModePen::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const
tip_color = glm::vec4(s.col, s.flow);
}
}
- ShaderManager::u_int(kShaderUniform::DrawOutline, glm::min(tip_scale.x, tip_scale.y) < 20 ? false : m_draw_outline);
+ ShaderManager::u_int(kShaderUniform::DrawOutline, glm::min(tip_scale.x, tip_scale.y) < 20 || m_resizing ? false : m_draw_outline);
ShaderManager::u_vec4(kShaderUniform::Col, tip_color);
ShaderManager::u_mat4(kShaderUniform::MVP,
glm::scale(glm::vec3(1, -1, 1)) *
diff --git a/src/main.cpp b/src/main.cpp
index 30546b1..98fe5b4 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -34,7 +34,6 @@ std::thread hmd_renderer;
std::thread renderer;
int running = -1;
std::mutex render_mutex;
-std::mutex wnd_mutex;
std::condition_variable render_cv;
int gl_count = 0;
@@ -687,12 +686,11 @@ int main(int argc, char** argv)
static wchar_t title_fps[512];
swprintf_s(title_fps, L"%s - %d fps", window_title, frames);
- // lock if
- if (wnd_mutex.try_lock())
- {
+ std::lock_guard lock(main_task_mutex);
+ main_tasklist.emplace_back([=] {
SetWindowText(hWnd, title_fps);
- wnd_mutex.unlock();
- }
+ });
+
one_sec = 0;
frames = 0;
}
@@ -869,18 +867,10 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
running = 0;
render_cv.notify_all();
-
- {
- // avoid deadlock
- // nobody should call windows API on this window at this time
- std::lock_guard lock(wnd_mutex);
-
- if (renderer.joinable())
- renderer.join();
- if (hmd_renderer.joinable())
- hmd_renderer.join();
- }
-
+ if (renderer.joinable())
+ renderer.join();
+ if (hmd_renderer.joinable())
+ hmd_renderer.join();
App::I.terminate();
}
break;
diff --git a/src/node.cpp b/src/node.cpp
index 6dfa25c..61a84b9 100644
--- a/src/node.cpp
+++ b/src/node.cpp
@@ -94,7 +94,9 @@ kEventResult Node::on_event(Event* e)
if (current_mouse_capture)
{
- if (e->m_cat == kEventCategory::MouseEvent && child_mouse_focus != current_mouse_capture)
+ if (e->m_cat == kEventCategory::MouseEvent &&
+ child_mouse_focus != current_mouse_capture &&
+ is_child(current_mouse_capture))
{
MouseEvent* me = static_cast(e);
if (child_mouse_focus)
@@ -432,6 +434,16 @@ bool Node::is_child_recursive(Node* o) const
return false;
}
+bool Node::is_child(Node* o) const
+{
+ for (const auto& c : m_children)
+ {
+ if (c.get() == o)
+ return true;
+ }
+ return false;
+}
+
void Node::mouse_capture()
{
auto& c = root()->current_mouse_capture;
diff --git a/src/node.h b/src/node.h
index dcd260e..84f4f67 100644
--- a/src/node.h
+++ b/src/node.h
@@ -259,6 +259,7 @@ public:
// returns {origin, size} form
glm::vec4 get_children_rect() const;
bool is_child_recursive(Node* o) const;
+ bool is_child(Node* o) const;
void mouse_capture();
void mouse_release();
void key_capture();
diff --git a/src/node_panel_brush.cpp b/src/node_panel_brush.cpp
index 8b6f90e..166a205 100644
--- a/src/node_panel_brush.cpp
+++ b/src/node_panel_brush.cpp
@@ -517,6 +517,7 @@ bool NodePanelBrushPreset::save()
i.m_jitter_hue = b->m_jitter_hue;
i.m_jitter_sat = b->m_jitter_sat;
i.m_jitter_val = b->m_jitter_val;
+ i.m_jitter_hsv_eachsample = b->m_jitter_hsv_eachsample;
i.m_jitter_aspect = b->m_jitter_aspect;
i.m_jitter_aspect_bothaxis = b->m_jitter_aspect_bothaxis;
i.m_blend_mode = b->m_blend_mode;
@@ -624,6 +625,7 @@ bool NodePanelBrushPreset::restore()
b->m_jitter_hue = i.m_jitter_hue;
b->m_jitter_sat = i.m_jitter_sat;
b->m_jitter_val = i.m_jitter_val;
+ b->m_jitter_hsv_eachsample = i.m_jitter_hsv_eachsample;
b->m_jitter_aspect = i.m_jitter_aspect;
b->m_jitter_aspect_bothaxis = i.m_jitter_aspect_bothaxis;
b->m_blend_mode = i.m_blend_mode;
diff --git a/src/node_panel_brush.h b/src/node_panel_brush.h
index 306c86f..2e8ba24 100644
--- a/src/node_panel_brush.h
+++ b/src/node_panel_brush.h
@@ -137,6 +137,7 @@ class NodePanelBrushPreset : public Node
float m_jitter_hue = 0;
float m_jitter_sat = 0;
float m_jitter_val = 0;
+ bool m_jitter_hsv_eachsample = false;
float m_jitter_aspect = 0;
bool m_jitter_aspect_bothaxis = false;
int m_blend_mode = 0;
diff --git a/src/node_panel_stroke.cpp b/src/node_panel_stroke.cpp
index 4c7e2cb..b7e6b28 100644
--- a/src/node_panel_stroke.cpp
+++ b/src/node_panel_stroke.cpp
@@ -163,6 +163,7 @@ void NodePanelStroke::update_controls()
m_jitter_hue->m_value = b->m_jitter_hue;
m_jitter_sat->m_value = b->m_jitter_sat;
m_jitter_val->m_value = b->m_jitter_val;
+ m_jitter_hsv_eachsample->checked = b->m_jitter_hsv_eachsample;
m_jitter_aspect->m_value = b->m_jitter_aspect;
m_tip_angle_follow->checked = b->m_tip_angle_follow;
m_tip_angle_init->checked = b->m_tip_angle_init;
@@ -484,6 +485,7 @@ void NodePanelStroke::init_controls()
init_checkbox(m_tip_size_pressure, "tip-size-pressure", &Brush::m_tip_size_pressure);
init_checkbox(m_jitter_scatter_bothaxis, "jitter-scatter-bothaxis", &Brush::m_jitter_scatter_bothaxis);
init_checkbox(m_jitter_aspect_bothaxis, "jitter-aspect-bothaxis", &Brush::m_jitter_aspect_bothaxis);
+ init_checkbox(m_jitter_hsv_eachsample, "jitter-hsv-eachsample", &Brush::m_jitter_hsv_eachsample);
init_checkbox(m_tip_invert, "tip-invert", &Brush::m_tip_invert);
init_checkbox(m_tip_flipx, "tip-flipx", &Brush::m_tip_flipx);
diff --git a/src/node_panel_stroke.h b/src/node_panel_stroke.h
index ef43570..00534a5 100644
--- a/src/node_panel_stroke.h
+++ b/src/node_panel_stroke.h
@@ -34,6 +34,7 @@ public:
NodeSliderH* m_jitter_hue;
NodeSliderH* m_jitter_sat;
NodeSliderH* m_jitter_val;
+ NodeCheckBox* m_jitter_hsv_eachsample;
NodeSliderH* m_jitter_aspect;
NodeCheckBox* m_tip_angle_init;
NodeCheckBox* m_tip_angle_follow;