diff --git a/src/canvas.cpp b/src/canvas.cpp index 73e9e75..a6937db 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -423,15 +423,29 @@ void ui::Canvas::stroke_draw() + dx + dy, // C - top-right + dx - dy, // D - bottom-right }; - static glm::vec4 P[4]; int intersected = 0; int inside = 0; + + // face is the 2d shape of the cube plane i projected onto the window space + auto face = face_to_shape2D(i); + // P is the initial square centered at the cursor location + std::vector P{ + 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 (int j = 0; j < 4; j++) + P[j].pos = glm::vec4(xy(s.pos) + off[j] * glm::orientate2(-s.angle), 1, 1); + // intersect P with the current face to clip diverging points from the plane + P = poly_intersect(P, face); + + for (int j = 0; j < P.size(); j++) { glm::vec3 ray_origin, ray_dir; if (s.pos.z == 0) { - point_unproject(xy(s.pos) + off[j] * glm::orientate2(-s.angle), { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir); + point_unproject(P[j].pos, { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir); } else { @@ -451,19 +465,29 @@ void ui::Canvas::stroke_draw() { inside++; } - P[j].x = -(plane_local.x * 0.5f - 0.5f) * m_width; - P[j].y = (plane_local.y * 0.5f + 0.5f) * m_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; + P[j].uvs2 = UV2[j]; + + // Black magic - BEWARE! + // interpolation perspective correction, use the current camera projection to correct the interpolation + // because the new shape will have z fixed with an ortho projection when drawn to the face + // we need to imitate the same perspective as the once in the camera + // see: https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/perspective-correct-interpolation-vertex-attributes + auto hit_cam = m_mv * glm::vec4(hit, 1); + P[j].pos.z = hit_cam.z; + P[j].uvs *= hit_cam.z; + P[j].uvs2 *= hit_cam.z; + intersected++; } else { -// if (i==0) -// LOG("no intersection with plane %d", i); break; } } - if (intersected < 4 || inside == 0) + if (intersected < 3 || inside == 0) continue; m_dirty_face[i] = true; @@ -473,13 +497,12 @@ void ui::Canvas::stroke_draw() glActiveTexture(GL_TEXTURE1); m_tex[i].bind(); // bg, copy of framebuffer (copied before drawing) - glm::vec2 bb_min(m_width, m_height); glm::vec2 bb_max(0, 0); - for (int j = 0; j < 4; j++) + for (int j = 0; j < P.size(); j++) { - bb_min = glm::max({ 0, 0 }, glm::min(bb_min, xy(P[j]))); - bb_max = glm::min({ m_width, m_height }, glm::max(bb_max, xy(P[j]))); + bb_min = glm::max({ 0, 0 }, glm::min(bb_min, xy(P[j].pos))); + bb_max = glm::min({ m_width, m_height }, glm::max(bb_max, xy(P[j].pos))); } auto bb_sz = bb_max - bb_min; @@ -498,11 +521,41 @@ void ui::Canvas::stroke_draw() glm::max(zw(m_dirty_box[i]), (glm::vec2)(tex_pos + tex_sz)) ); + ShaderManager::use(ui::kShader::Stroke); ShaderManager::u_mat4(kShaderUniform::MVP, ortho_proj); ShaderManager::u_vec4(kShaderUniform::Col, glm::vec4(s.col, m_brush.m_tip_color.a)); ShaderManager::u_float(kShaderUniform::Alpha, s.flow); - m_plane_brush.update_vertices(P, nullptr, UV2); - m_plane_brush.draw_fill(); + + P = triangulate_simple(P); + m_brush_shape.update_vertices(P.data(), P.size()); + 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(); +*/ glActiveTexture(GL_TEXTURE1); m_tex[i].unbind(); @@ -950,6 +1003,7 @@ bool ui::Canvas::create(int width, int height) m_sampler_mix.create(GL_NEAREST, GL_REPEAT); m_plane.create<1>(1, 1); m_plane_brush.create<1>(1, 1); + m_brush_shape.create(); m_mesh.create(); m_brush_mix.create(8, 8); for (auto& l : m_layers) @@ -2039,8 +2093,8 @@ std::vector ui::Canvas::face_to_shape2D(int plane_index) { auto pt_clip = m_proj * glm::vec4(p, 1); pt_clip = pt_clip / pt_clip.w; - glm::vec2 pt_screen = (glm::vec2(pt_clip) * glm::vec2(1,-1) * 0.5f + 0.5f) * zw(m_vp); - //pt_screen.y = m_vp.w - pt_screen.y - 1; + glm::vec2 pt_screen = (glm::vec2(pt_clip) * 0.5f + 0.5f) * zw(m_vp); + pt_screen.y = m_vp.w - pt_screen.y - 1; points.push_back(pt_screen); } return points; @@ -2065,11 +2119,8 @@ std::vector ui::Canvas::triangulate_simple(const std::vector vertex_t vertex; for (int i = 0; i < 3; i++) { - auto p = t->GetPoint(i); - auto index = std::distance(points.data(), p); - vertex.pos = glm::vec4(p->x, p->y, 0, 1); - vertex.uvs = vertices[index].uvs; - ret.push_back(vertex); + auto index = std::distance(points.data(), t->GetPoint(i)); + ret.push_back(vertices[index]); } } diff --git a/src/canvas.h b/src/canvas.h index 886813d..f0c546a 100644 --- a/src/canvas.h +++ b/src/canvas.h @@ -105,6 +105,7 @@ class Canvas public: Plane m_plane; Plane m_plane_brush; + DynamicShape m_brush_shape; BrushMesh m_mesh; bool m_unsaved = false; bool m_newdoc = true; @@ -129,6 +130,8 @@ public: bool m_show_tmp = false; std::vector m_layers; std::vector m_order; + glm::mat4 m_plane_unproject[6] = SIXPLETTE(glm::mat4(1)); + glm::vec3 m_plane_dir[6] = SIXPLETTE(glm::vec3(0)); glm::vec4 m_dirty_box[6] = SIXPLETTE(glm::vec4(0)); bool m_dirty_face[6] = SIXPLETTE(false); Layer m_smask; // selection mask diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp index f150b28..3316445 100644 --- a/src/canvas_modes.cpp +++ b/src/canvas_modes.cpp @@ -874,9 +874,19 @@ void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, m_circle.draw_fill(); } - ui::ShaderManager::use(ui::kShader::UVs); + ui::ShaderManager::use(ui::kShader::StrokePreview); + ui::ShaderManager::u_int(ui::kShaderUniform::Tex, 0); + ui::ShaderManager::u_float(ui::kShaderUniform::Alpha, canvas->m_current_brush.m_tip_flow); + auto tip_color = glm::vec4(glm::vec3(canvas->m_current_brush.m_tip_color), 1); + ui::ShaderManager::u_vec4(ui::kShaderUniform::Col, tip_color); ui::ShaderManager::u_mat4(ui::kShaderUniform::MVP, ortho); + glEnable(GL_BLEND); + glActiveTexture(GL_TEXTURE0); + auto& tex = TextureManager::get(canvas->m_current_brush.m_tex_id); + tex.bind(); + canvas->m_sampler.bind(0); m_shape.draw_fill(); + tex.unbind(); if (depth) glEnable(GL_DEPTH_TEST); } @@ -887,6 +897,80 @@ void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { case kEventType::MouseDownR: //m_origin = canvas->project2Dpoint(loc); + { + auto p = loc; + // flip the y coordinate to match opengl Y up + p.y = canvas->m_box.w - loc.y - 1; + //LOG("loc %f %f", p.x, p.y); + auto ln = (p / zw(canvas->m_box)) * 2.f - 1.f; + + glm::vec3 ro, rd, hit_o, hit_d; + glm::vec2 hit_fb; + canvas->point_trace_plane(loc, ro , rd, hit_o, hit_d, hit_fb, 0); + + //LOG("hit plane %f %f %f", hit_o.x, hit_o.y, hit_o.z); + auto plane2cam = canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0] * glm::vec4(hit_o, 1); + plane2cam = plane2cam / plane2cam.w; + + auto p3d_back = glm::inverse(canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0]) * plane2cam; + p3d_back = p3d_back / p3d_back.w; + //LOG("\nloc nor %f %f", ln.x, ln.y); + //LOG("plane2cam %f %f %f %f", plane2cam.x, plane2cam.y, plane2cam.z, plane2cam.w); + //LOG("\ncam2plane inv %f %f %f %f", p3d_back.x, p3d_back.y, p3d_back.z, p3d_back.w); + + auto loc_back = glm::inverse(canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0]) * glm::vec4(ln, 1, 1); + loc_back = loc_back / loc_back.z; + //LOG("loc inv %f %f %f %f", loc_back.x, loc_back.y, loc_back.z, loc_back.w); + + auto p2d = (xy(plane2cam) * 0.5f + 0.5f) * zw(canvas->m_box); + p2d.y = canvas->m_box.w - p2d.y - 1; + //LOG("vp %f %f", p2d.x, p2d.y); + + auto loc_vp = (xy(loc_back) * glm::vec2(-1, -1) * 0.5f + 0.5f) * glm::vec2(canvas->m_width, canvas->m_height); + + LOG("trc fb %f %f", hit_fb.x, hit_fb.y); + LOG("prj vp %f %f", loc_vp.x, loc_vp.y); + + LOG("trc dir %f %f %f", hit_d.x, hit_d.y, hit_d.z); + LOG("prj dir %f %f %f", canvas->m_plane_dir[0].x, canvas->m_plane_dir[0].y, canvas->m_plane_dir[0].z); + + LOG("------------------------------------------"); + // convert to normalized cube space + //auto p3d = glm::vec4((p / zw(canvas->m_box)) * 2.f - 1.f, 1, 1); + //auto m = glm::inverse(canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0]); + //p3d = p3d * m; + //p3d = p3d / p3d.w; + //auto p3d = glm::unProject(glm::vec3(loc, 1), canvas->m_mv * canvas->m_plane_transform[0], canvas->m_proj, canvas->m_vp); + //p3d = p3d / p3d.z; + +/* + { + double sum = 0; + auto start = std::chrono::steady_clock::now(); + for (int i = 0; i < 10000000; i++) + { + glm::vec3 ro, rd, hit_o, hit_d; + glm::vec2 hit_fb; + canvas->point_trace_plane(loc, ro, rd, hit_o, hit_d, hit_fb, 0); + sum += hit_fb.x; + } + auto t1 = std::chrono::steady_clock::now(); + auto m = glm::inverse(canvas->m_proj * canvas->m_mv * canvas->m_plane_transform[0]); + for (int i = 0; i < 10000000; i++) + { + auto loc_back = m * glm::vec4(ln, 1, 1); + loc_back = loc_back / loc_back.z; + //auto loc_vp = (xy(loc_back) * glm::vec2(-1, -1) * 0.5f + 0.5f) * glm::vec2(canvas->m_width, canvas->m_height); + sum += loc_back.x; + } + auto t2 = std::chrono::steady_clock::now(); + LOG("t1 %d", (int)std::chrono::duration_cast(t1 - start).count()); + LOG("t2 %d", (int)std::chrono::duration_cast(t2 - t1).count()); + LOG("sum %f", sum); + } +*/ + + } break; case kEventType::MouseMove: case kEventType::MouseUpL: diff --git a/src/node_canvas.cpp b/src/node_canvas.cpp index e0c354b..6a6a36b 100644 --- a/src/node_canvas.cpp +++ b/src/node_canvas.cpp @@ -101,6 +101,8 @@ void NodeCanvas::draw() for (int plane_index = 0; plane_index < 6; plane_index++) { + m_canvas->m_plane_unproject[plane_index] = glm::inverse(m_canvas->m_proj * m_canvas->m_mv * m_canvas->m_plane_transform[0]); + m_canvas->m_plane_dir[plane_index] = -(m_canvas->m_plane_transform[plane_index] * glm::vec4(m_canvas->m_plane_origin[plane_index], 1)); auto plane_mvp = proj * camera * glm::scale(glm::vec3(m_canvas->m_order.size() + 500)) * m_canvas->m_plane_transform[plane_index] * diff --git a/src/util.cpp b/src/util.cpp index d83ac5b..27e7043 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -119,7 +119,7 @@ std::vector poly_intersect(const std::vector& poly, const st glm::vec2 hit_uv; segments_intersect(edge[0], edge[1], s[0].pos, s[1].pos, pt, hit_uv); vertex_t v; - v.pos = glm::vec4(pt, 0, 1); + v.pos = glm::lerp(s[0].pos, s[1].pos, hit_uv.y); v.uvs = glm::lerp(s[0].uvs, s[1].uvs, hit_uv.y); v.uvs2 = glm::lerp(s[0].uvs2, s[1].uvs2, hit_uv.y); if (side0) // outgoing