diff --git a/src/app_shaders.cpp b/src/app_shaders.cpp index 2d66fd9..3875387 100644 --- a/src/app_shaders.cpp +++ b/src/app_shaders.cpp @@ -63,6 +63,24 @@ " return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n"\ "}\n" +#define SHADER_FUNCTION_COLOR \ + "mediump vec3 brightness3(mediump vec3 c, mediump float val) {\n"\ + " return c + vec3(val * 2.0 - 1.0);\n"\ + "}\n"\ + "mediump vec3 contrast3(mediump vec3 c, mediump float val) {\n"\ + " val = val * 2.0 - 1.0;\n"\ + " mediump float factor = ((259.0 / 255.0) * (val + 1.0)) / (1.0 * ((259.0 / 255.0) - val));\n"\ + " return factor * (c - 0.5) + 0.5;\n"\ + "}\n"\ + "mediump float brightness1(mediump float c, mediump float val) {\n"\ + " return c + (val * 2.0 - 1.0);\n"\ + "}\n"\ + "mediump float contrast1(mediump float c, mediump float val) {\n"\ + " val = val * 2.0 - 1.0;\n"\ + " mediump float factor = ((259.0 / 255.0) * (val + 1.0)) / (1.0 * ((259.0 / 255.0) - val));\n"\ + " return factor * (c - 0.5) + 0.5;\n"\ + "}\n" + // http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/ #define SHADER_FUNCTION_RAND \ "highp float rand(mediump vec2 co)\n"\ @@ -226,6 +244,9 @@ void App::initShaders() "uniform mediump int blend_mode;\n" "uniform mediump int dual_blend_mode;\n" "uniform mediump vec2 resolution;\n" + "uniform mediump vec2 pattern_scale;\n" + "uniform mediump float pattern_bright;\n" + "uniform mediump float pattern_contr;\n" "uniform bool lock;\n" "uniform bool mask;\n" "uniform bool fragUV2;\n" @@ -236,12 +257,18 @@ void App::initShaders() SHADER_FUNCTION_BLUR SHADER_FUNCTION_BLEND SHADER_FUNCTION_BLEND_STROKE + SHADER_FUNCTION_COLOR "void main() {\n" " mediump vec2 uv2 = fragUV2 ? (gl_FragCoord.st / resolution) : uv;\n" " mediump vec4 base = texture(tex, uv2);\n" " mediump vec4 stroke = texture(tex_stroke, uv);\n" " if (use_pattern){\n" - " stroke.a *= 1.0 - texture(tex_pattern, uv2 * 5.0).r * pattern_alpha;\n" + " mediump float patt = texture(tex_pattern, uv2 * pattern_scale * 10.0).r;\n" + " if (pattern_bright != 0.5)\n" + " patt = brightness1(patt, pattern_bright);\n" + " if (pattern_contr != 0.5)\n" + " patt = contrast1(patt, pattern_contr);\n" + " stroke.a = mix(stroke.a, stroke.a * patt, pattern_alpha);\n" " }\n" " if (use_dual){\n" " mediump vec4 dual = texture(tex_dual, uv);\n" @@ -453,6 +480,7 @@ void App::initShaders() "}\n"; static const char* shader_checkerboard_f = SHADER_VERSION + "uniform bool colorize;\n" "in mediump vec2 uv;\n" "out mediump vec4 frag;\n" "void main() {\n" @@ -460,7 +488,8 @@ void App::initShaders() " const mediump vec4 c2 = vec4(0.9, 0.9, 0.9, 1.0);\n" " mediump vec2 c = floor(fract(uv * 10.0) * 2.0);\n" " mediump float alpha = mix(c.x, 1.0 - c.x, c.y);\n" - " frag = mix(c1, c2, alpha);\n" + " if (colorize) frag = mix(c1, c2, alpha) * vec4(fract(uv.x * 5.0), uv.y, 1.0, 1.0);\n" + " else frag = mix(c1, c2, alpha);\n" "}\n"; static const char* shader_equirect_v = diff --git a/src/app_vr.cpp b/src/app_vr.cpp index 5bd8024..8d5a037 100644 --- a/src/app_vr.cpp +++ b/src/app_vr.cpp @@ -33,6 +33,7 @@ void App::vr_draw(const glm::mat4& proj, const glm::mat4& camera, const glm::mat glm::translate(glm::vec3(0, 0, -1)); ShaderManager::use(kShader::Checkerboard); + ShaderManager::u_int(kShaderUniform::Colorize, false); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); } @@ -102,6 +103,10 @@ 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_float(kShaderUniform::PatternBright, b->m_pattern_brightness); + ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast); + glActiveTexture(GL_TEXTURE0); canvas->m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); glActiveTexture(GL_TEXTURE1); diff --git a/src/canvas.cpp b/src/canvas.cpp index 2fad26c..07eeefe 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -798,6 +798,9 @@ 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_float(kShaderUniform::PatternBright, b->m_pattern_brightness); + ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast); glActiveTexture(GL_TEXTURE0); m_tex2[i].bind(); @@ -1007,7 +1010,7 @@ void Canvas::layer_merge(int source_idx, int dest_idx) // m_layer index ShaderManager::u_int(kShaderUniform::BlendMode, m_layers[source_idx].m_blend_mode); ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); ShaderManager::u_int(kShaderUniform::UseDual, false); - ShaderManager::u_int(kShaderUniform::UsePattern,false); + ShaderManager::u_int(kShaderUniform::UsePattern, false); glActiveTexture(GL_TEXTURE0); m_tex2[i].bind(); @@ -2517,174 +2520,6 @@ void Canvas::set_camera(const CameraData& c) m_vp = c.m_vp; } -std::vector Canvas::triangulate_simple(const std::vector& vertices) -{ - std::vector ret; - std::vector points(vertices.size()); - std::vector points_ptr(vertices.size()); - for (size_t i = 0; i < vertices.size(); i++) - { - points[i] = { vertices[i].pos.x, vertices[i].pos.y }; - points_ptr[i] = &points[i]; - } - - auto cdt = std::make_unique(points_ptr); - cdt->Triangulate(); - auto tr = cdt->GetTriangles(); - for (auto t : tr) - { - vertex_t vertex; - for (int i = 0; i < 3; i++) - { - auto index = std::distance(points.data(), t->GetPoint(i)); - ret.push_back(vertices[index]); - } - } - - return ret; -} - -std::vector Canvas::triangulate(const std::vector& points) -{ - std::vector tmp; - for (auto pt : points) - tmp.push_back(pt); - return triangulate(tmp); -} - -std::vector Canvas::triangulate(const std::vector& points) -{ - struct Segment - { - const vertex_t* a = nullptr; - const vertex_t* b = nullptr; - Segment* prev = nullptr; - std::shared_ptr next = nullptr; - bool end = false; - }; - std::vector> new_points; - std::shared_ptr root = std::make_shared(); - std::shared_ptr node = root; - for (int i = 0; i < points.size(); i++) - { - node->a = &points[i]; - if (i == points.size() - 1) - { - node->b = &points[0]; - node->next = root; - node->end = true; - root->prev = node.get(); - } - else - { - node->b = &points[i + 1]; - node->next = std::make_shared(); - node->next->prev = node.get(); - } - node = node->next; - } - - node = root; - std::stack> todo; - std::vector> polys; - todo.push(root); - while (!todo.empty()) - { - node = todo.top(); - todo.pop(); - polys.push_back(node); - while (node) - { - std::shared_ptr other = node->next; - while (other) - { - if (node->a->pos == other->a->pos || node->a->pos == other->b->pos || - node->b->pos == other->a->pos || node->b->pos == other->b->pos) - { - other = other->end ? nullptr : other->next; - continue; - } - glm::vec2 s0a(node->a->pos); - glm::vec2 s0b(node->b->pos); - glm::vec2 s1a(other->a->pos); - glm::vec2 s1b(other->b->pos); - glm::vec2 hit_uv; - glm::vec2 is; - if (segments_intersect(s0a, s0b, s1a, s1b, is, hit_uv)) - { - new_points.push_back(std::make_unique()); - auto p = new_points.back().get(); - p->pos = glm::lerp(node->a->pos, node->b->pos, hit_uv.x); - p->uvs = glm::lerp(node->a->uvs, node->b->uvs, hit_uv.x); - p->uvs2 = glm::lerp(node->a->uvs2, node->b->uvs2, hit_uv.x); - auto poly_root = std::make_shared(); - poly_root->a = p; - poly_root->b = node->b; - poly_root->next = node->next; - todo.push(poly_root); - other->a = p; - node->b = p; - auto poly_end = std::make_shared(); - poly_end->a = other->prev->b; - poly_end->b = p; - poly_end->end = true; - poly_end->prev = other->prev; - other->prev->next = poly_end; - other->prev = node.get(); - node->next = other; - break; - } - other = other->end ? nullptr : other->next; - } - node = node->end ? nullptr : node->next; - } - } - - std::vector ret; - for (auto poly : polys) - { - std::vector outline; - node = poly; - while (node) - { - if (outline.empty() || // if empty insert right away - outline.back() != node->a && // insert only if different than the last post - (outline.front() != node->a || !node->end)) // if is the end check against the first one - { - outline.push_back(node->a); - } - auto current = node; - node = node->end ? nullptr : node->next; - current->next = nullptr; - } - - if (outline.size() > 2) - { - std::vector points(outline.size()); - std::vector points_ptr(outline.size()); - for (size_t i = 0; i < outline.size(); i++) - { - points[i] = { outline[i]->pos.x, outline[i]->pos.y }; - points_ptr[i] = &points[i]; - } - - p2t::CDT* cdt = new p2t::CDT(points_ptr); // TODO: remove duplicates - cdt->Triangulate(); - auto tr = cdt->GetTriangles(); - for (auto t : tr) - { - for (int i = 0; i < 3; i++) - { - auto index = std::distance(points.data(), t->GetPoint(i)); - ret.push_back(*outline[index]); - } - } - } - } - - return ret; -} - /////////////////////////////////////////////////////////////////////////////////////////// void Layer::destroy() diff --git a/src/canvas.h b/src/canvas.h index 9e54d6e..cef249c 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -276,9 +276,6 @@ public: glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id); bool point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id); - std::vector triangulate_simple(const std::vector& vertices); - std::vector triangulate(const std::vector& points); - std::vector triangulate(const std::vector& points); void project2Dpoints(std::vector& vertices); glm::vec3 project2Dpoint(glm::vec2 pt); std::vector face_to_shape2D(int plane_index); diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp index c5db5ac..6c5a4bf 100644 --- a/src/canvas_modes.cpp +++ b/src/canvas_modes.cpp @@ -521,7 +521,7 @@ void CanvasModeMaskFree::on_MouseEvent(MouseEvent* me, glm::vec2& loc) m_shape.draw_fill(); }; // use m_shape to render the mask polygon - auto v = Canvas::I->triangulate(poly_remove_duplicate(m_points2d)); + auto v = triangulate(poly_remove_duplicate(m_points2d)); Canvas::I->project2Dpoints(v); m_shape.update_vertices(v.data(), (int)v.size()); Canvas::I->draw_objects_direct(std::bind(drawer, std::placeholders::_1, std::placeholders::_2), Canvas::I->m_smask); @@ -629,7 +629,7 @@ void CanvasModeMaskLine::leave() std::vector points; for (int i = 0; i < (int)m_points2d.size(); i++) points.emplace_back(m_points2d[i]); - auto v = Canvas::I->triangulate(poly_remove_duplicate(points)); + auto v = triangulate(poly_remove_duplicate(points)); Canvas::I->project2Dpoints(v); LOG("%d points", (int)v.size()); @@ -922,7 +922,7 @@ void CanvasModeTransform::enter(kCanvasMode prev) vertex_t(corners[1], { 1, 1 }), vertex_t(corners[3], { 0, 1 }), }); - auto shape3d = Canvas::I->triangulate(m_points_face[0]); + auto shape3d = triangulate(m_points_face[0]); m_shape[0].update_vertices(shape3d.data(), shape3d.size()); m_commit_on_leave = true; @@ -1028,7 +1028,7 @@ void CanvasModeTransform::enter(kCanvasMode prev) v.pos = center_mat * v.pos; } - auto shape3d = Canvas::I->triangulate(m_points_face[plane]); + auto shape3d = triangulate(m_points_face[plane]); m_shape[plane].update_vertices(shape3d.data(), shape3d.size()); Canvas::I->m_layers[Canvas::I->m_current_layer_idx].m_rtt[plane].bindFramebuffer(); diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index bb18163..4143e02 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -138,6 +138,7 @@ void NodeCanvas::draw() glm::translate(glm::vec3(0, 0, -1)); ShaderManager::use(kShader::Checkerboard); + ShaderManager::u_int(kShaderUniform::Colorize, false); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); } @@ -221,6 +222,9 @@ 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_float(kShaderUniform::PatternBright, b->m_pattern_brightness); + ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast); glActiveTexture(GL_TEXTURE0); m_canvas->m_layers[layer_index].m_rtt[plane_index].bindTexture(); @@ -353,6 +357,7 @@ void NodeCanvas::draw() glm::translate(glm::vec3(0, 0, -1.f)); ShaderManager::use(kShader::Checkerboard); + ShaderManager::u_int(kShaderUniform::Colorize, false); ShaderManager::u_mat4(kShaderUniform::MVP, plane_mvp); m_face_plane.draw_fill(); } diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index b7ad314..964c696 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -34,6 +34,7 @@ void NodeStrokePreview::init_controls() m_sampler.create(GL_LINEAR, GL_REPEAT); m_sampler_brush.create(); m_sampler_brush.set_filter(GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR); + m_brush_shape.create(); // TextureManager::load("data/thumbs/Round-Hard.png"); // Canvas::I->m_current_brush.m_tex_id = const_hash("data/thumbs/Round-Hard.png"); } @@ -53,84 +54,367 @@ void NodeStrokePreview::clear_context() m_rtt.destroy(); } +void NodeStrokePreview::stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz) +{ + gl_state gl; + gl.save(); + + m_rtt_mixer.bindFramebuffer(); + + glViewport(0, 0, m_rtt_mixer.getWidth(), m_rtt_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); + + const auto& b = m_brush; + ShaderManager::use(kShader::CompDraw); + ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_int(kShaderUniform::TexStroke, 1); + ShaderManager::u_int(kShaderUniform::TexMask, 2); + ShaderManager::u_int(kShaderUniform::TexDual, 3); + ShaderManager::u_int(kShaderUniform::TexPattern, 4); + //ShaderManager::u_vec2(kShaderUniform::Resolution, m_size); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, b->m_tip_opacity); + ShaderManager::u_float(kShaderUniform::PatternAlpha, b->m_pattern_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, 1); + ShaderManager::u_int(kShaderUniform::Lock, false); + ShaderManager::u_int(kShaderUniform::Mask, false); + ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); + ShaderManager::u_int(kShaderUniform::BlendMode, b->m_blend_mode); + ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); + 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_float(kShaderUniform::PatternBright, b->m_pattern_brightness); + ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast); + + glActiveTexture(GL_TEXTURE0); + m_tex_background.bind(); + glActiveTexture(GL_TEXTURE1); + m_tex.bind(); + glActiveTexture(GL_TEXTURE3); + m_tex_dual.bind(); + glActiveTexture(GL_TEXTURE4); + if (b->m_pattern_texture) + b->m_pattern_texture->bind(); + m_plane.draw_fill(); + + m_rtt_mixer.unbindFramebuffer(); + gl.restore(); +} + +glm::vec4 NodeStrokePreview::stroke_draw_samples(std::array& P, Texture2D& blend_tex) +{ + if (!ShaderManager::ext_framebuffer_fetch) + { + glActiveTexture(GL_TEXTURE1); + blend_tex.bind(); // bg, copy of framebuffer (copied before drawing) + } + + glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() }; + + glm::vec2 bb_min(size); + glm::vec2 bb_max(0, 0); + for (int j = 0; j < P.size(); j++) + { + bb_min = glm::max({ 0, 0 }, glm::min(bb_min, xy(P[j].pos))); + bb_max = glm::min(size, glm::max(bb_max, xy(P[j].pos))); + } + auto bb_sz = bb_max - bb_min; + + glm::vec2 pad(1); + glm::ivec2 tex_pos = glm::clamp(glm::floor(bb_min) - pad, { 0, 0 }, size); + glm::ivec2 tex_sz = glm::clamp(glm::ceil(bb_sz) + pad * 2.f, { 0, 0 }, (glm::vec2)(glm::ivec2(size) - tex_pos)); + if (!ShaderManager::ext_framebuffer_fetch) + { + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, tex_pos.x, tex_pos.y, + tex_pos.x, tex_pos.y, tex_sz.x, tex_sz.y); + } + + if (P.size() == 4) + { + static vertex_t rect[6]; + rect[0] = P[0]; + rect[1] = P[1]; + rect[2] = P[2]; + rect[3] = P[0]; + rect[4] = P[2]; + rect[5] = P[3]; + m_brush_shape.update_vertices(rect, 6); + } + m_brush_shape.draw_fill(); + + if (!ShaderManager::ext_framebuffer_fetch) + { + glActiveTexture(GL_TEXTURE1); + blend_tex.unbind(); + } + + return glm::vec4(tex_pos, tex_pos + tex_sz); +} + +std::vector NodeStrokePreview::stroke_draw_compute(Stroke& stroke) const +{ + std::vector ret; + StrokeSample prev = stroke.m_prev_sample; + auto samples = stroke.compute_samples(); + std::array B = { + vertex_t{ {0, 0, 1, 1}, {0, 0}, {0, 0} }, + vertex_t{ {0, 0, 1, 1}, {0, 1}, {0, 1} }, + vertex_t{ {0, 0, 1, 1}, {1, 1}, {1, 1} }, + vertex_t{ {0, 0, 1, 1}, {1, 0}, {1, 0} }, + }; + for (const auto& s : samples) + { + if (!s.valid()) + continue; + + ret.emplace_back(); + auto& f = ret.back(); + + glm::vec2 dx_mix(prev.size * 0.5f, 0), dy_mix(0, prev.size * 0.5f); + glm::vec2 off_mix[4] = { + -dx_mix - dy_mix, // A - bottom-left + -dx_mix + dy_mix, // B - top-left + +dx_mix + dy_mix, // C - top-right + +dx_mix - dy_mix, // D - bottom-right + }; + // P is the initial square centered at the cursor location + glm::vec2 dx(s.size * 0.5f, 0), dy(0, s.size * 0.5f); + glm::vec2 off[4] = { + -dx - dy, // A - bottom-left + -dx + dy, // B - top-left + +dx + dy, // C - top-right + +dx - dy, // D - bottom-right + }; + + glm::vec2 mixer_sz(m_rtt.getWidth(), m_rtt.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(prev.pos) + s.scale * off_mix[j] * glm::orientate2(-s.angle)); + 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) + s.scale * off[j] * glm::orientate2(-s.angle) - glm::vec2(0, 1), 1, 1); + B[j].uvs2 = p / mixer_sz; + } + + f.m_mixer_rect = { glm::floor(mixer_bb_min), glm::ceil(mixer_bb_max - mixer_bb_min) }; + f.col = glm::vec4(s.col, 1); + f.pressure = s.flow; + f.shapes = B; + + prev = s; + } + return ret; +} + void NodeStrokePreview::draw_stroke() { + if (m_size.x == 0 || m_size.y == 0) + return; + + GLint vp[4]; + GLfloat cc[4]; + glGetIntegerv(GL_VIEWPORT, vp); + glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); + + glm::vec2 size = { m_rtt.getWidth(), m_rtt.getHeight() }; + glm::mat4 ortho_proj = glm::ortho(0, size.x, 0, size.y, -1, 1); + glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); m_rtt.bindFramebuffer(); + m_sampler.bind(0); + m_sampler.bind(1); + m_sampler.bind(2); + m_sampler.bind(3); + m_sampler.bind(4); + + const auto& b = m_brush; + m_stroke.m_filter_points = false; + m_stroke.m_max_size = m_size.y / 800.f * App::I.zoom; + m_stroke.m_camera.fov = Canvas::I->m_cam_fov; + m_stroke.m_camera.rot = Canvas::I->m_cam_rot; + m_stroke.reset(true); + m_stroke.start(b); + + auto dual_brush = std::make_shared(); + dual_brush->m_tip_flow = b->m_dual_flow; + dual_brush->m_tip_opacity = b->m_dual_opacity; + dual_brush->m_tip_flipx = b->m_dual_flipx; + dual_brush->m_tip_flipy = b->m_dual_flipy; + dual_brush->m_tip_invert = b->m_dual_invert; + dual_brush->m_blend_mode = b->m_dual_blend_mode; + dual_brush->m_tip_randflipx = b->m_dual_randflip; + dual_brush->m_tip_randflipy = b->m_dual_randflip; + dual_brush->m_tip_size = b->m_dual_size; + dual_brush->m_tip_spacing = b->m_dual_spacing; + dual_brush->m_jitter_spread = b->m_dual_scatter; + dual_brush->m_jitter_angle = b->m_dual_rotate; + dual_brush->m_tip_texture = b->m_dual_texture; + m_dual_stroke.m_filter_points = false; + m_dual_stroke.m_camera.fov = Canvas::I->m_cam_fov; + m_dual_stroke.m_camera.rot = Canvas::I->m_cam_rot; + m_dual_stroke.reset(true); + m_dual_stroke.start(dual_brush); + { - GLint vp[4]; - GLfloat cc[4]; - glGetIntegerv(GL_VIEWPORT, vp); - glGetFloatv(GL_COLOR_CLEAR_VALUE, cc); - - glClearColor(1, 1, 1, 1); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(0, 0, m_rtt.getWidth(), m_rtt.getHeight()); - glEnable(GL_BLEND); - glm::mat4 proj = glm::ortho(0, (float)m_rtt.getWidth(), 0, (float)m_rtt.getHeight(), -1, 1); - - const auto& b = m_brush; - m_stroke.m_filter_points = false; - m_stroke.m_max_size = m_size.y / 800.f * App::I.zoom; - m_stroke.m_camera.fov = Canvas::I->m_cam_fov; - m_stroke.m_camera.rot = Canvas::I->m_cam_rot; - m_stroke.reset(); - m_stroke.start(b); - if (!m_stroke.m_keypoints.empty()) - m_stroke.m_prev_sample.origin = m_stroke.m_keypoints[0].pos; - auto samples = m_stroke.compute_samples(); - auto& tex = *b->m_tip_texture; - glActiveTexture(GL_TEXTURE0); - tex.bind(); - m_sampler_brush.bind(0); - - glActiveTexture(GL_TEXTURE1); - if (b->m_pattern_texture && b->m_pattern_enabled) - b->m_pattern_texture->bind(); - else - glBindTexture(GL_TEXTURE_2D, 0); - m_sampler.bind(1); - - if (true) + float w = m_size.x; + float h = m_size.y; + float pad = m_size.x * .15f; + std::vector kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } }; + for (int i = 0; i < 20; i++) { - ShaderManager::use(kShader::BrushStroke); - ShaderManager::u_vec4(kShaderUniform::Col, { 0, 0, 0, 1 }); - ShaderManager::u_int(kShaderUniform::Tex, 0); - ShaderManager::u_int(kShaderUniform::TexPattern, 1); // stencil - ShaderManager::u_vec2(kShaderUniform::Resolution, { m_rtt.getWidth(), m_rtt.getHeight() }); - ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(0)); - ShaderManager::u_float(kShaderUniform::PatternAlpha, b->m_pattern_opacity); - m_mesh.draw(samples, proj); + float t = (float)i / 20.f; + float p = 1.f - glm::abs(t * 2.f - 1.f); + m_stroke.add_point(glm::vec3(BezierCurve::Bezier2D(kp, t), 0), p); + m_dual_stroke.add_point(glm::vec3(BezierCurve::Bezier2D(kp, t), 0), p); } - //else - //{ - // ShaderManager::use("stroke"); - // ShaderManager::u_vec4(kShaderUniform::Col, m_brush->m_tip_color); - // ShaderManager::u_int(kShaderUniform::Tex, 0); - // for (const auto& s : samples) - // { - // auto mvp = proj * - // glm::translate(glm::vec3(s.pos, 0)) * - // glm::scale(glm::vec3(s.size, s.size, 1)) * - // glm::eulerAngleZ(s.angle); - // ShaderManager::u_mat4(kShaderUniform::MVP, mvp); - // ShaderManager::u_float(kShaderUniform::Alpha, s.flow); - // m_plane.draw_fill(); - // } - //} - - m_sampler_brush.unbind(); - m_sampler.unbind(); - tex.unbind(); - glDisable(GL_BLEND); - - glViewport(vp[0], vp[1], vp[2], vp[3]); - glClearColor(cc[0], cc[1], cc[2], cc[3]); } + + glDisable(GL_BLEND); + ShaderManager::use(kShader::Stroke); + ShaderManager::u_int(kShaderUniform::Tex, 0); // brush + if (!ShaderManager::ext_framebuffer_fetch) + ShaderManager::u_int(kShaderUniform::TexBG, 1); // bg + ShaderManager::u_int(kShaderUniform::TexPattern, 2); // pattern + ShaderManager::u_int(kShaderUniform::TexMix, 3); // mixer + //ShaderManager::u_int(kShaderUniform::TexMixA, 4); // mixer + ShaderManager::u_vec2(kShaderUniform::Resolution, size); + ShaderManager::u_vec2(kShaderUniform::PatternOffset, glm::vec2(0)); + ShaderManager::u_float(kShaderUniform::PatternAlpha, b->m_pattern_opacity); + ShaderManager::u_int(kShaderUniform::UsePattern, b->m_pattern_enabled && b->m_pattern_eachsample); + ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj); + + // DRAW DUAL BRUSH + + if (b->m_dual_enabled) + { + m_rtt.clear(); + ShaderManager::use(kShader::Stroke); + ShaderManager::u_int(kShaderUniform::UsePattern, false); + ShaderManager::u_float(kShaderUniform::MixAlpha, 0); + ShaderManager::u_float(kShaderUniform::Wet, 0); + ShaderManager::u_float(kShaderUniform::Noise, 0); + glActiveTexture(GL_TEXTURE0); + dual_brush->m_tip_texture->bind(); + auto frames_dual = stroke_draw_compute(m_dual_stroke); + for (auto& f : frames_dual) + { + ShaderManager::u_vec4(kShaderUniform::Col, f.col); + ShaderManager::u_float(kShaderUniform::Alpha, f.pressure); + auto rect = stroke_draw_samples(f.shapes, m_tex_dual); + } + glActiveTexture(GL_TEXTURE0); + dual_brush->m_tip_texture->unbind(); + + // copy raw stroke to tex + glActiveTexture(GL_TEXTURE1); + m_tex_dual.bind(); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.x, size.y); + } + + // DRAW MAIN BRUSH + + ShaderManager::use(kShader::Stroke); + ShaderManager::u_float(kShaderUniform::MixAlpha, b->m_tip_mix); + ShaderManager::u_float(kShaderUniform::Wet, b->m_tip_wet); + ShaderManager::u_float(kShaderUniform::Noise, b->m_tip_noise); + + glActiveTexture(GL_TEXTURE0); + b->m_tip_texture->bind(); + glActiveTexture(GL_TEXTURE1); + m_tex.bind(); // tmp swap for blending + glActiveTexture(GL_TEXTURE2); + if (b->m_pattern_texture) + b->m_pattern_texture->bind(); + glActiveTexture(GL_TEXTURE3); + m_rtt_mixer.bindTexture(); + auto frames = stroke_draw_compute(m_stroke); + m_rtt.clear(); + for (auto& f : frames) + { + if (b->m_tip_mix > 0.f) + { + stroke_draw_mix(xy(f.m_mixer_rect), zw(f.m_mixer_rect)); + } + + ShaderManager::use(kShader::Stroke); + ShaderManager::u_vec4(kShaderUniform::Col, f.col); + ShaderManager::u_float(kShaderUniform::Alpha, f.pressure); + auto rect = stroke_draw_samples(f.shapes, m_tex); + } + glActiveTexture(GL_TEXTURE3); + m_rtt_mixer.unbindTexture(); + + // copy raw stroke to tex + glActiveTexture(GL_TEXTURE1); + m_tex.bind(); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.x, size.y); + + // CHEKCERBOARD + + // copy background color to tex2 + ShaderManager::use(kShader::Checkerboard); + ShaderManager::u_int(kShaderUniform::Colorize, b->m_tip_mix > 0.f); + float aspect = size.x / size.y; + ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f / aspect, .5f / aspect, -1.f, 1.f)); + m_plane.draw_fill(); + m_tex_background.bind(); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.x, size.y); + + // COMPOSITE + + ShaderManager::use(kShader::CompDraw); + ShaderManager::u_int(kShaderUniform::Tex, 0); + ShaderManager::u_int(kShaderUniform::TexStroke, 1); + ShaderManager::u_int(kShaderUniform::TexMask, 2); + ShaderManager::u_int(kShaderUniform::TexDual, 3); + ShaderManager::u_int(kShaderUniform::TexPattern, 4); + //ShaderManager::u_vec2(kShaderUniform::Resolution, m_size); + ShaderManager::u_float(kShaderUniform::StrokeAlpha, b->m_tip_opacity); + ShaderManager::u_float(kShaderUniform::PatternAlpha, b->m_pattern_opacity); + ShaderManager::u_float(kShaderUniform::Alpha, 1); + ShaderManager::u_int(kShaderUniform::Mask, false); + ShaderManager::u_int(kShaderUniform::UseFragCoordUV2, false); + ShaderManager::u_int(kShaderUniform::BlendMode, b->m_blend_mode); + ShaderManager::u_mat4(kShaderUniform::MVP, glm::ortho(-.5f, .5f, -.5f, .5f, -1.f, 1.f)); + 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_float(kShaderUniform::PatternBright, b->m_pattern_brightness); + ShaderManager::u_float(kShaderUniform::PatternContrast, b->m_pattern_contrast); + + m_sampler.bind(0); + m_sampler.bind(1); + m_sampler.bind(2); + m_sampler.bind(3); + m_sampler.bind(4); + + glActiveTexture(GL_TEXTURE0); + m_tex_background.bind(); + glActiveTexture(GL_TEXTURE1); + m_tex.bind(); + glActiveTexture(GL_TEXTURE3); + m_tex_dual.bind(); + glActiveTexture(GL_TEXTURE4); + if (b->m_pattern_texture) + b->m_pattern_texture->bind(); + m_plane.draw_fill(); + m_rtt.unbindFramebuffer(); + glViewport(vp[0], vp[1], vp[2], vp[3]); + glClearColor(cc[0], cc[1], cc[2], cc[3]); } void NodeStrokePreview::draw() { + //glEnable(GL_BLEND); ShaderManager::use(kShader::Texture); ShaderManager::u_mat4(kShaderUniform::MVP, m_mvp); ShaderManager::u_int(kShaderUniform::Tex, 0); @@ -149,19 +433,33 @@ void NodeStrokePreview::handle_resize(glm::vec2 old_size, glm::vec2 new_size) float pad = m_size.x * .15f; new_size *= root()->m_zoom; - m_rtt.destroy(); m_rtt.create((int)new_size.x, (int)new_size.y); + m_rtt_mixer.create((int)new_size.x, (int)new_size.y); + m_tex.create((int)new_size.x, (int)new_size.y); + m_tex_dual.create((int)new_size.x, (int)new_size.y); + m_tex_background.create((int)new_size.x, (int)new_size.y); float w = new_size.x; float h = new_size.y; - std::vector kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } }; + m_stroke.m_filter_points = false; + m_stroke.m_max_size = m_size.y / 800.f * App::I.zoom; + m_stroke.m_camera.fov = Canvas::I->m_cam_fov; + m_stroke.m_camera.rot = Canvas::I->m_cam_rot; m_stroke.reset(true); m_stroke.start(m_brush); + m_dual_stroke.m_filter_points = false; + //m_dual_stroke.m_max_size = m_size.y / 800.f * App::I.zoom; + m_dual_stroke.m_camera.fov = Canvas::I->m_cam_fov; + m_dual_stroke.m_camera.rot = Canvas::I->m_cam_rot; + m_dual_stroke.reset(true); + m_dual_stroke.start(m_brush); + std::vector kp = { { pad, pad },{ pad, h - pad },{ w - pad, pad },{ w - pad, h - pad } }; for (int i = 0; i < 20; i++) { float t = (float)i / 20.f; float p = 1.f - glm::abs(t * 2.f - 1.f); m_stroke.add_point(glm::vec3(BezierCurve::Bezier2D(kp, t), 0), p); + m_dual_stroke.add_point(glm::vec3(BezierCurve::Bezier2D(kp, t), 0), p); } draw_stroke(); diff --git a/src/node_stroke_preview.h b/src/node_stroke_preview.h index e2afb5a..ce9215e 100644 --- a/src/node_stroke_preview.h +++ b/src/node_stroke_preview.h @@ -6,13 +6,28 @@ class NodeStrokePreview : public NodeBorder { + struct StrokeFrame + { + glm::vec4 col; + float pressure; + std::array shapes; + glm::vec4 m_mixer_rect; + }; + RTT m_rtt; + RTT m_rtt_mixer; + Texture2D m_tex; // blending tmp texture + Texture2D m_tex_dual; + Texture2D m_tex_background; Sampler m_sampler; Sampler m_sampler_brush; BrushMesh m_mesh; + DynamicShape m_brush_shape; public: std::shared_ptr m_brush; + std::shared_ptr m_dual_brush; Stroke m_stroke; + Stroke m_dual_stroke; std::vector m_bez_points; virtual Node* clone_instantiate() const override; virtual void clone_copy(Node* dest) const override; @@ -21,6 +36,10 @@ public: void init_controls(); virtual void restore_context() override; virtual void clear_context() override; + void stroke_draw_mix(const glm::vec2& bb_min, const glm::vec2& bb_sz); + // return rect {origin, size} + glm::vec4 stroke_draw_samples(std::array& P, Texture2D& blend_tex); + std::vector stroke_draw_compute(Stroke& stroke) const; void draw_stroke(); virtual void draw() override; virtual void handle_resize(glm::vec2 old_size, glm::vec2 new_size) override; diff --git a/src/shader.h b/src/shader.h index 796dede..71878e0 100644 --- a/src/shader.h +++ b/src/shader.h @@ -37,6 +37,10 @@ enum class kShaderUniform : uint16_t LightDir = const_hash("light_dir"), Mode = const_hash("mode"), Ambient = const_hash("ambient"), + PatternScale = const_hash("pattern_scale"), + PatternBright = const_hash("pattern_bright"), + PatternContrast = const_hash("pattern_contr"), + Colorize = const_hash("colorize"), }; enum class kShader : uint16_t diff --git a/src/util.cpp b/src/util.cpp index 0f34d66..f9ccdb8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "log.h" #include "util.h" +#include template<> std::vector poly_remove_duplicate(const std::vector& v, const float tollerance) @@ -236,6 +237,176 @@ std::vector poly_clip_near(const std::vector& poly, float return poly_remove_duplicate(ret); } + +std::vector triangulate_simple(const std::vector& vertices) +{ + std::vector ret; + std::vector points(vertices.size()); + std::vector points_ptr(vertices.size()); + for (size_t i = 0; i < vertices.size(); i++) + { + points[i] = { vertices[i].pos.x, vertices[i].pos.y }; + points_ptr[i] = &points[i]; + } + + auto cdt = std::make_unique(points_ptr); + cdt->Triangulate(); + auto tr = cdt->GetTriangles(); + for (auto t : tr) + { + vertex_t vertex; + for (int i = 0; i < 3; i++) + { + auto index = std::distance(points.data(), t->GetPoint(i)); + ret.push_back(vertices[index]); + } + } + + return ret; +} + +std::vector triangulate(const std::vector& points) +{ + std::vector tmp; + for (auto pt : points) + tmp.push_back(pt); + return triangulate(tmp); +} + +std::vector triangulate(const std::vector& points) +{ + struct Segment + { + const vertex_t* a = nullptr; + const vertex_t* b = nullptr; + Segment* prev = nullptr; + std::shared_ptr next = nullptr; + bool end = false; + }; + std::vector> new_points; + std::shared_ptr root = std::make_shared(); + std::shared_ptr node = root; + for (int i = 0; i < points.size(); i++) + { + node->a = &points[i]; + if (i == points.size() - 1) + { + node->b = &points[0]; + node->next = root; + node->end = true; + root->prev = node.get(); + } + else + { + node->b = &points[i + 1]; + node->next = std::make_shared(); + node->next->prev = node.get(); + } + node = node->next; + } + + node = root; + std::stack> todo; + std::vector> polys; + todo.push(root); + while (!todo.empty()) + { + node = todo.top(); + todo.pop(); + polys.push_back(node); + while (node) + { + std::shared_ptr other = node->next; + while (other) + { + if (node->a->pos == other->a->pos || node->a->pos == other->b->pos || + node->b->pos == other->a->pos || node->b->pos == other->b->pos) + { + other = other->end ? nullptr : other->next; + continue; + } + glm::vec2 s0a(node->a->pos); + glm::vec2 s0b(node->b->pos); + glm::vec2 s1a(other->a->pos); + glm::vec2 s1b(other->b->pos); + glm::vec2 hit_uv; + glm::vec2 is; + if (segments_intersect(s0a, s0b, s1a, s1b, is, hit_uv)) + { + new_points.push_back(std::make_unique()); + auto p = new_points.back().get(); + p->pos = glm::lerp(node->a->pos, node->b->pos, hit_uv.x); + p->uvs = glm::lerp(node->a->uvs, node->b->uvs, hit_uv.x); + p->uvs2 = glm::lerp(node->a->uvs2, node->b->uvs2, hit_uv.x); + auto poly_root = std::make_shared(); + poly_root->a = p; + poly_root->b = node->b; + poly_root->next = node->next; + todo.push(poly_root); + other->a = p; + node->b = p; + auto poly_end = std::make_shared(); + poly_end->a = other->prev->b; + poly_end->b = p; + poly_end->end = true; + poly_end->prev = other->prev; + other->prev->next = poly_end; + other->prev = node.get(); + node->next = other; + break; + } + other = other->end ? nullptr : other->next; + } + node = node->end ? nullptr : node->next; + } + } + + std::vector ret; + for (auto poly : polys) + { + std::vector outline; + node = poly; + while (node) + { + if (outline.empty() || // if empty insert right away + outline.back() != node->a && // insert only if different than the last post + (outline.front() != node->a || !node->end)) // if is the end check against the first one + { + outline.push_back(node->a); + } + auto current = node; + node = node->end ? nullptr : node->next; + current->next = nullptr; + } + + if (outline.size() > 2) + { + std::vector points(outline.size()); + std::vector points_ptr(outline.size()); + for (size_t i = 0; i < outline.size(); i++) + { + points[i] = { outline[i]->pos.x, outline[i]->pos.y }; + points_ptr[i] = &points[i]; + } + + p2t::CDT* cdt = new p2t::CDT(points_ptr); // TODO: remove duplicates + cdt->Triangulate(); + auto tr = cdt->GetTriangles(); + for (auto t : tr) + { + for (int i = 0; i < 3; i++) + { + auto index = std::distance(points.data(), t->GetPoint(i)); + ret.push_back(*outline[index]); + } + } + } + } + + return ret; +} + + glm::vec4 rand_color() { float r = (rand() % 256) / 256.f; diff --git a/src/util.h b/src/util.h index 18e7eba..3aacc63 100644 --- a/src/util.h +++ b/src/util.h @@ -63,6 +63,9 @@ bool point_side(glm::vec2 a, glm::vec2 b, glm::vec2 p); std::vector poly_intersect(const vertex_t* poly_begin, const vertex_t* poly_end, const std::vector& clip); std::vector poly_intersect(const std::vector& poly, const std::vector& clip); std::vector poly_clip_near(const std::vector& poly, float near_plane_distance); +std::vector triangulate(const std::vector& points); +std::vector triangulate(const std::vector& points); +std::vector triangulate_simple(const std::vector& vertices); glm::vec4 rand_color(); glm::vec3 convert_hsv2rgb(const glm::vec3 c); glm::vec3 convert_rgb2hsv(const glm::vec3 c);