From 171ab31b4711cd4e44a7199aea240bebaa2169a9 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Mon, 11 Feb 2019 18:39:26 +0100 Subject: [PATCH] code cleanup and improve brush direction --- src/app_shaders.cpp | 30 +++++----- src/brush.cpp | 48 +++++++++++---- src/brush.h | 9 ++- src/canvas.cpp | 142 +++++++++----------------------------------- src/util.h | 12 +++- 5 files changed, 97 insertions(+), 144 deletions(-) diff --git a/src/app_shaders.cpp b/src/app_shaders.cpp index 8e514f7..e987b55 100644 --- a/src/app_shaders.cpp +++ b/src/app_shaders.cpp @@ -27,13 +27,15 @@ "{ return mix(stroke.rgb, mix(base.rgb, base.rgb/(1.0-stroke.rgb), stroke.a/alpha_tot), base.a/alpha_tot); }\n"\ "mediump vec3 blend_overlay(mediump vec4 base, mediump vec4 stroke, mediump float alpha_tot)"\ "{ return mix(stroke.rgb, mix(base.rgb, mix(2.0*base.rgb*stroke.rgb, 1.0-2.0*(1.0-base.rgb)*(1.0-stroke.rgb), floor(base.rgb*2.0)), stroke.a/alpha_tot), base.a/alpha_tot); }\n"\ - "mediump vec3 blend(mediump vec4 base, mediump vec4 stroke, mediump float alpha_tot, int mode) {\n"\ - " if (mode == 0) return blend_normal(base, stroke, alpha_tot);\n"\ - " else if (mode == 1) return blend_multiply(base, stroke, alpha_tot);\n"\ - " else if (mode == 2) return blend_screen(base, stroke, alpha_tot);\n"\ - " else if (mode == 3) return blend_colorDodge(base, stroke, alpha_tot);\n"\ - " else if (mode == 4) return blend_overlay(base, stroke, alpha_tot);\n"\ - " else return blend_multiply(base, stroke, alpha_tot);\n"\ + "mediump vec4 blend(mediump vec4 base, mediump vec4 stroke, int mode) {\n"\ + " mediump float contribution = (1.0 - base.a) * stroke.a;\n"\ + " mediump float alpha_tot = base.a + contribution;\n"\ + " if (mode == 0) return vec4(blend_normal(base, stroke, alpha_tot), alpha_tot);\n"\ + " else if (mode == 1) return vec4(blend_multiply(base, stroke, alpha_tot), alpha_tot);\n"\ + " else if (mode == 2) return vec4(blend_screen(base, stroke, alpha_tot), alpha_tot);\n"\ + " else if (mode == 3) return vec4(blend_colorDodge(base, stroke, alpha_tot), alpha_tot);\n"\ + " else if (mode == 4) return vec4(blend_overlay(base, stroke, alpha_tot), alpha_tot);\n"\ + " else return vec4(1, 0, 0, 1);\n"\ "}\n" #define SHADER_FUNCTION_HSV \ @@ -115,9 +117,8 @@ void App::initShaders() "#endif\n" " mediump vec4 fg = vec4(texture(tex, uv).rgb, texture(tex_alpha, uv).a);\n" " if (fg.a == 0.0) { frag = bg; return; }\n" - " mediump float contribution = (1.0 - bg.a) * fg.a;\n" - " mediump float alpha_tot = bg.a + contribution;\n" - " frag = vec4(blend(bg, fg, alpha_tot, blend_mode), alpha_tot * alpha);\n" + " mediump vec4 blended = blend(bg, fg, blend_mode);\n" + " frag = vec4(blended.rgb, blended.a * alpha);\n" "}\n"; static const char* shader_uv_f = SHADER_VERSION @@ -207,14 +208,17 @@ void App::initShaders() "uniform sampler2D tex;\n" "uniform sampler2D tex_stroke;\n" "uniform sampler2D tex_mask;\n" + "uniform sampler2D tex_dual;\n" //"uniform sampler2D tex_stencil;\n" "uniform mediump float alpha;\n" "uniform mediump float stroke_alpha;\n" "uniform mediump int blend_mode;\n" + "uniform mediump int dual_blend_mode;\n" "uniform mediump vec2 resolution;\n" "uniform bool lock;\n" "uniform bool mask;\n" "uniform bool fragUV2;\n" + "uniform bool useDual;\n" "in mediump vec2 uv;\n" "out mediump vec4 frag;\n" SHADER_FUNCTION_BLUR @@ -225,10 +229,8 @@ void App::initShaders() " mediump vec4 stroke = texture(tex_stroke, uv);\n" " stroke.a = mask ? stroke.a * stroke_alpha * blur(tex_mask, uv2).r : stroke.a * stroke_alpha;\n" " if (!lock && base.a == 0.0) { frag = stroke * vec4(1.0, 1.0, 1.0, alpha); return; }\n" - " mediump float contribution = (1.0 - base.a) * stroke.a;\n" - " mediump float alpha_tot = base.a + contribution;\n" - " mediump vec3 rgb = blend(base, stroke, alpha_tot, blend_mode);\n" - " frag = vec4(rgb, (lock ? base.a : alpha_tot) * alpha);\n" + " mediump vec4 blended = blend(base, stroke, blend_mode);\n" + " frag = vec4(blended.rgb, (lock ? base.a : blended.a) * alpha);\n" "}\n"; // TEXTURE ATLAS diff --git a/src/brush.cpp b/src/brush.cpp index cd7dd5c..75d1735 100644 --- a/src/brush.cpp +++ b/src/brush.cpp @@ -161,6 +161,7 @@ std::vector Stroke::compute_samples() { bool is_first = m_last_kp == 0; m_dist += m_step; + m_dir_dist += m_step; while (m_dist > m_keypoints[m_last_kp + 1].dist) m_last_kp++; const auto& A = m_keypoints[m_last_kp]; @@ -168,28 +169,41 @@ std::vector Stroke::compute_samples() float t = (m_dist - A.dist) / (B.dist - A.dist); // NOTE: must be A != B auto pos = glm::lerp(A.pos, B.pos, t); float pressure = glm::lerp(A.pressure, B.pressure, t); - + auto s = randomize_sample(pos, pressure, 0); if (s.valid()) { if (m_brush->m_tip_angle_follow) { - glm::vec2 v = s.origin - m_prev_sample.origin; - if (v.length() > 0) + if (m_dir_dist > m_dir_step) { - m_direction.add((v)); - auto avg = m_direction.average(); - float curve_angle = -glm::orientedAngle(glm::normalize(avg), glm::vec2(1, 0)); + glm::vec2 v = glm::normalize(m_keypoints[m_last_kp].pos - m_keypoints[m_dir_kp].pos); + m_dir_angle = -glm::orientedAngle(v, m_dir_ref); + if (glm::abs(m_dir_angle) > glm::pi() / 2.f || !m_dir_valid) + { + m_direction.clear(); + m_dir_ref = v; + m_dir_ref_angle = -glm::orientedAngle(m_dir_ref, { 1, 0 }); + m_dir_angle = 0; + } + m_dir_kp = m_last_kp; + m_dir_dist = 0; + m_dir_valid = true; + } - // NOTE: average angles need correction for 0-360 discontinuity - //m_curve_angles.add(curve_angle); - //float avg = m_curve_angles.average(); - - s.angle += curve_angle; + if (m_dir_valid) + { + m_direction.add(m_dir_angle); + s.angle += m_direction.average() + m_dir_ref_angle; + m_prev_sample = s; + samples.push_back(s); } } - m_prev_sample = s; - samples.push_back(s); + else + { + m_prev_sample = s; + samples.push_back(s); + } } else { @@ -205,6 +219,10 @@ bool Stroke::has_sample() } void Stroke::reset(bool clear_keypoints /*= false*/) { + m_dir_kp = 0; + m_dir_angle = 0; + m_dir_valid = false; + m_dir_dist = 0; m_last_kp = 0; m_dist = 0.f; if (clear_keypoints) @@ -251,6 +269,10 @@ void Stroke::start(const std::shared_ptr& brush) m_hsv_jitter.clear(); m_last_kp = 0; m_dist = 0.f; + m_dir_kp = 0; + m_dir_angle = 0; + m_dir_valid = false; + m_dir_dist = 0; m_brush = brush; float size = glm::min(m_brush->m_tip_size / glm::tan(glm::radians(m_camera.fov * 0.5f)), m_max_size); m_step = glm::max(m_brush->m_tip_spacing * size * 800.f, 1.f); diff --git a/src/brush.h b/src/brush.h index 013cd87..41896ab 100644 --- a/src/brush.h +++ b/src/brush.h @@ -96,6 +96,13 @@ public: float fov = 0; }; int m_layer = 0; + int m_dir_kp = 0; + bool m_dir_valid = false; + glm::vec2 m_dir_ref = { 1, 0 }; + float m_dir_ref_angle = 0; + float m_dir_dist = 0; + float m_dir_step = 10; + float m_dir_angle = 0; float m_curve = 0; float m_dist = 0; float m_step = 0; @@ -103,7 +110,7 @@ public: bool m_filter_points = true; Camera m_camera; std::shared_ptr m_brush; - cbuffer m_direction; + cbuffer m_direction; cbuffer m_pressure_buff; cbuffer m_hsv_jitter; StrokeSample m_prev_sample; diff --git a/src/canvas.cpp b/src/canvas.cpp index 18667d3..83a5d1c 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -210,18 +210,18 @@ void Canvas::stroke_cancel() } void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) { + gl_state gl; + gl.save(); + m_mixer.bindFramebuffer(); - float zoom = m_node->root()->m_zoom; glViewport(0, 0, m_mixer.getWidth(), m_mixer.getHeight()); glDisable(GL_DEPTH_TEST); glEnable(GL_SCISSOR_TEST); + glDisable(GL_BLEND); glScissor(bb_min.x, bb_min.y, bb_sz.x, bb_sz.y); - m_mixer.clear({ 1, 1, 1, 0 }); - m_sampler.bind(0); - m_sampler_linear.bind(1); auto layer_index = m_current_layer_idx; for (int plane_index = 0; plane_index < 6; plane_index++) { @@ -237,36 +237,6 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) m_plane_transform[plane_index] * glm::translate(glm::vec3(0, 0, -1)); - ShaderManager::use(kShader::TextureAlphaSep); - ShaderManager::u_int(kShaderUniform::Tex, 0); - ShaderManager::u_int(kShaderUniform::TexA, 1); - ShaderManager::u_float(kShaderUniform::Alpha, m_layers[layer_index].m_opacity); - ShaderManager::u_int(kShaderUniform::Highlight, false); - ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); - - - glEnable(GL_BLEND); - glActiveTexture(GL_TEXTURE0); - m_layers[layer_index].m_rtt[plane_index].bindTexture(); - glActiveTexture(GL_TEXTURE1); - m_layers[layer_index].m_rtt[plane_index].bindTexture(); - m_node->m_face_plane.draw_fill(); - glActiveTexture(GL_TEXTURE1); - m_layers[layer_index].m_rtt[plane_index].unbindTexture(); - glActiveTexture(GL_TEXTURE0); - m_layers[layer_index].m_rtt[plane_index].unbindTexture(); - - glEnable(GL_BLEND); -// glActiveTexture(GL_TEXTURE0); -// m_tmp[plane_index].bindTexture(); -// glActiveTexture(GL_TEXTURE1); -// m_tmp[plane_index].bindTexture(); -// m_node->m_face_plane.draw_fill(); -// glActiveTexture(GL_TEXTURE1); -// m_tmp[plane_index].unbindTexture(); -// glActiveTexture(GL_TEXTURE0); -// m_tmp[plane_index].unbindTexture(); - m_sampler.bind(0); m_sampler.bind(1); m_sampler.bind(2); @@ -279,8 +249,8 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) //ShaderManager::u_int(kShaderUniform::TexStencil, 3); ShaderManager::u_float(kShaderUniform::StrokeAlpha, m_current_stroke->m_brush->m_tip_opacity); ShaderManager::u_float(kShaderUniform::Alpha, 1); - ShaderManager::u_int(kShaderUniform::Lock, m_layers[layer_index].m_alpha_locked); - ShaderManager::u_int(kShaderUniform::Mask, m_smask_active); + ShaderManager::u_int(kShaderUniform::Lock, false/*m_layers[layer_index].m_alpha_locked*/); + ShaderManager::u_int(kShaderUniform::Mask, false/*m_smask_active*/); ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); ShaderManager::u_int(kShaderUniform::BlendMode, m_current_stroke->m_brush->m_blend_mode); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp_z); @@ -290,15 +260,7 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) m_tmp[plane_index].bindTexture(); glActiveTexture(GL_TEXTURE2); m_smask.m_rtt[plane_index].bindTexture(); - glActiveTexture(GL_TEXTURE3); - if (m_current_stroke->m_brush->m_stencil_texture) - m_current_stroke->m_brush->m_stencil_texture->bind(); - else - glBindTexture(GL_TEXTURE_2D, 0); m_node->m_face_plane.draw_fill(); - if (m_current_stroke->m_brush->m_stencil_texture) - m_current_stroke->m_brush->m_stencil_texture->unbind(); - glActiveTexture(GL_TEXTURE2); m_smask.m_rtt[plane_index].unbindTexture(); glActiveTexture(GL_TEXTURE1); m_tmp[plane_index].unbindTexture(); @@ -307,7 +269,8 @@ void Canvas::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) } m_sampler.unbind(); m_mixer.unbindFramebuffer(); - glDisable(GL_SCISSOR_TEST); + + gl.restore(); } void Canvas::stroke_draw() { @@ -386,7 +349,6 @@ void Canvas::stroke_draw() m_mixer_idle = false; } - static glm::vec2 UV2[4]; glm::vec2 dx_mix(m_mixer_sample.size * 0.5f, 0), dy_mix(0, m_mixer_sample.size * 0.5f); glm::vec2 off_mix[4] = { -dx_mix - dy_mix, // A - bottom-left @@ -402,60 +364,43 @@ void Canvas::stroke_draw() +dx + dy, // C - top-right +dx - dy, // D - bottom-right }; - auto sz = glm::vec2(m_mixer.getWidth(), m_mixer.getHeight()) * m_mixer_scale; - glm::vec2 bb_min(sz); - glm::vec2 bb_max(0, 0); + + glm::vec2 mixer_sz(m_mixer.getWidth(), m_mixer.getHeight()); + glm::vec2 mixer_bb_min(mixer_sz); + glm::vec2 mixer_bb_max(0, 0); for (int j = 0; j < 4; j++) { - auto p = (xy(m_mixer_sample.pos) + off_mix[j] * glm::orientate2(-s.angle) + glm::vec2(0, 1)); - UV2[j] = p / sz; - bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p)); - bb_max = glm::min(sz, glm::max(bb_max, p)); + auto p = (xy(m_mixer_sample.pos) + off_mix[j] * glm::orientate2(-s.angle) + glm::vec2(0, 1)); + mixer_bb_min = glm::max({ 0, 0 }, glm::min(mixer_bb_min, p)); + mixer_bb_max = glm::min(mixer_sz, glm::max(mixer_bb_max, p)); B[j].pos = glm::vec4(xy(s.pos) + off[j] * glm::orientate2(-s.angle), 1, 1); - B[j].uvs2 = UV2[j]; + B[j].uvs2 = p / mixer_sz; } - auto bb_sz = bb_max - bb_min; if (m_brush->m_tip_mix > 0.f) { - stroke_draw_mix(bb_min, bb_sz); - - glViewport(0, 0, m_width, m_height); - - glActiveTexture(GL_TEXTURE0); - tex.bind(); - m_sampler_brush.bind(0); - m_sampler_bg.bind(1); - m_sampler_stencil.bind(2); - m_sampler.bind(3); - //m_sampler_linear.bind(5); - - glActiveTexture(GL_TEXTURE2); - if (m_brush->m_stencil_texture) - m_brush->m_stencil_texture->bind(); - else - glBindTexture(GL_TEXTURE_2D, 0); - glActiveTexture(GL_TEXTURE3); - m_mixer.bindTexture(); - glDisable(GL_BLEND); + stroke_draw_mix(mixer_bb_min, mixer_bb_max - mixer_bb_min); } for (int i = 0; i < 6; i++) { +/* // check if plane is even visible glm::vec4 forward = m_mv * glm::vec4(0, 0, 1, 1); float dot = glm::dot(xyz(forward), m_plane_normal[i]); // TODO: use better threshold than 0.3 // some trigonometric shit, tangent and stuff - // if (dot < -0.3f) - // continue; + if (dot < -0.3f) + continue; +*/ + int intersected = 0; - int inside = 0; // intersect P with the current face to clip diverging points from the plane auto P = poly_intersect(B, m_plane_shape[i]); + glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]); for (int j = 0; j < P.size(); j++) { glm::vec3 ray_origin, ray_dir; @@ -481,12 +426,8 @@ void Canvas::stroke_draw() float hit_t; if (ray_intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit, hit_t)) { - glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]); glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); - if (glm::abs(plane_local.x) < 1.5f && glm::abs(plane_local.y) < 1.5f) - { - inside++; - } + //P[j].uvs2 = xy(P[j].pos) / glm::vec2(App::I.width, App::I.height); P[j].pos.x = -(plane_local.x * 0.5f - 0.5f) * m_width; P[j].pos.y = (plane_local.y * 0.5f + 0.5f) * m_height; @@ -510,7 +451,7 @@ void Canvas::stroke_draw() } } - if (intersected < 3 || inside == 0) + if (intersected < 3) continue; m_dirty_face[i] = true; @@ -541,10 +482,8 @@ void Canvas::stroke_draw() tex_pos.x, tex_pos.y, tex_sz.x, tex_sz.y); } - m_dirty_box[i] = glm::vec4( - glm::min(xy(m_dirty_box[i]), (glm::vec2)tex_pos), - glm::max(zw(m_dirty_box[i]), (glm::vec2)(tex_pos + tex_sz)) - ); + m_dirty_box[i] = glm::vec4(glm::min(xy(m_dirty_box[i]), (glm::vec2)tex_pos), + glm::max(zw(m_dirty_box[i]), (glm::vec2)(tex_pos + tex_sz))); ShaderManager::use(kShader::Stroke); ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj); @@ -573,33 +512,6 @@ void Canvas::stroke_draw() } m_brush_shape.draw_fill(); -/* - // draw sample wireframe - std::vector lines; - for (int vi = 0; vi < P.size(); vi += 3) - { - auto a = P[vi]; - auto b = P[(vi + 1) % P.size()]; - auto c = P[(vi + 2) % P.size()]; - a.pos.z = b.pos.z = c.pos.z = 0; - - lines.push_back(a); - lines.push_back(b); - - lines.push_back(b); - lines.push_back(c); - - lines.push_back(c); - lines.push_back(a); - } - - ShaderManager::use(kShader::Color); - ShaderManager::u_vec4(kShaderUniform::Col, { s.col, 1 }); - ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj); - m_brush_shape.update_vertices(lines.data(), lines.size()); - m_brush_shape.draw_stroke(); -*/ - if (!ShaderManager::ext_framebuffer_fetch) { glActiveTexture(GL_TEXTURE1); diff --git a/src/util.h b/src/util.h index 2bcb3c6..48332a1 100644 --- a/src/util.h +++ b/src/util.h @@ -95,7 +95,7 @@ template struct cbuffer m_index = 0; m_count = 0; } - T& head() + const T& head() const { return m_index == 0 ? m_vec[m_count - 1] : m_vec[m_index - 1]; } @@ -118,6 +118,16 @@ template struct cbuffer tot += m_vec[i]; return tot / (float)m_count; } + template T2 average_threshold(T threshold) const + { + T2 tot{}; + if (m_count == 0) + return tot; + int n = 0; + for (int i = 0; i < m_count; i++) + tot += glm::abs(m_vec[i] - head()) < threshold ? 0 : m_vec[i], n++; + return tot / (float)n; + } }; template