diff --git a/engine/brush.h b/engine/brush.h index a51950e..dca4439 100644 --- a/engine/brush.h +++ b/engine/brush.h @@ -54,9 +54,15 @@ public: float pressure; float dist; }; + struct Camera + { + glm::vec2 rot; + float fov; + }; int m_layer; float m_dist; float m_step; + Camera m_camera; ui::Brush m_brush; std::vector m_keypoints; std::vector m_samples; diff --git a/engine/canvas.cpp b/engine/canvas.cpp index 1e75c91..35107b7 100644 --- a/engine/canvas.cpp +++ b/engine/canvas.cpp @@ -72,8 +72,46 @@ void ui::Canvas::stroke_draw() ShaderManager::u_vec2(kShaderUniform::Resolution, { m_width, m_height }); for (const auto& s : samples) { + auto unproject = [](glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj, glm::vec3& out_origin, glm::vec3& out_dir) { + auto clip_space = glm::vec2(loc.x, vp.w - loc.y - 1.f) / vp.zw() * 2.f - 1.f; + auto inv = glm::inverse(proj * camera); + auto wp0 = inv * glm::vec4(clip_space, 0, 1); + auto wp1 = inv * glm::vec4(clip_space, .5, 1); + out_origin = (wp0 / wp0.w).xyz(); + out_dir = glm::normalize((wp1 / wp1.w).xyz() - out_origin); + }; + auto intersect = [](glm::vec3 ray_origin, glm::vec3 ray_dir, glm::vec3 plane_origin, glm::vec3 plane_normal, glm::vec3& out_hit) { + float den = glm::dot(ray_dir, plane_normal); + if (den == 0) + return false; // no intersection + float num = glm::dot(plane_origin - ray_origin, plane_normal); + float t = num / den; + if (t > 0) + out_hit = ray_origin + ray_dir * t; + else + // negative intersection + return false; + return true; + }; + glm::vec3 ray_origin, ray_dir; + unproject(s.pos, { 0, 0, m_box.zw }, m_mv, m_proj, ray_origin, ray_dir); + glm::vec3 plane_origin{ 0, 0, -1 }, plane_dir{ 0, 0, 1 }; + glm::vec3 hit; + glm::vec2 fb_pos; + if (intersect(ray_origin, ray_dir, plane_origin, plane_dir, hit)) + { + glm::mat4 plane_camera = glm::lookAt(plane_origin, plane_dir, { 0, 1, 0 }); + glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); + if (glm::abs(plane_local.x) < 0.5f && glm::abs(plane_local.y) < 0.5f) + { + fb_pos.x = -(plane_local.x - 0.5f) * m_width; + fb_pos.y = (plane_local.y + 0.5f) * m_height; + LOG("draw %f %f", fb_pos.x, fb_pos.y); + } + } + auto mvp = proj * - glm::translate(glm::vec3(s.pos, 0)) * + glm::translate(glm::vec3(fb_pos, 0)) * glm::scale(glm::vec3(s.size, s.size, 1)) * glm::eulerAngleZ(s.angle); @@ -112,10 +150,10 @@ void ui::Canvas::stroke_draw() m_box.xy = glm::min(m_box.xy(), (glm::vec2)tex_pos); m_box.zw = glm::max(m_box.zw(), (glm::vec2)(tex_pos + tex_sz)); - ShaderManager::u_mat4(kShaderUniform::MVP, mvp); + ShaderManager::u_mat4(kShaderUniform::MVP, glm::mat4()); ShaderManager::u_float(kShaderUniform::Alpha, s.flow); - //m_plane.update_vertices(P); - m_plane.draw_fill(); + m_plane_brush.update_vertices(P); + m_plane_brush.draw_fill(); } } @@ -206,6 +244,7 @@ void ui::Canvas::stroke_update(glm::vec2 point, float pressure) void ui::Canvas::stroke_start(glm::vec2 point, float pressure, const ui::Brush& brush) { m_current_stroke = std::make_unique(); + m_current_stroke->m_camera = { m_cam_rot, m_cam_fov }; m_current_stroke->start(brush); m_current_stroke->add_point(point, pressure); @@ -260,6 +299,7 @@ bool ui::Canvas::create(int width, int height) m_sampler.create(); m_sampler_bg.create(); m_plane.create<1>(1, 1); + m_plane_brush.create<1>(1, 1); m_mesh.create(); return true; } diff --git a/engine/canvas.h b/engine/canvas.h index 88a94ef..2cb9086 100644 --- a/engine/canvas.h +++ b/engine/canvas.h @@ -11,11 +11,13 @@ NS_START class Canvas { Plane m_plane; + Plane m_plane_brush; BrushMesh m_mesh; bool m_dirty = false; public: bool m_erase = false; - glm::mat4 m_mvp; + glm::mat4 m_mv; + glm::mat4 m_proj; glm::vec4 m_box; int m_width; int m_height; @@ -30,6 +32,8 @@ public: Texture2D m_tex2; Sampler m_sampler; Sampler m_sampler_bg; + glm::vec2 m_cam_rot; + float m_cam_fov = 85; bool create(int width, int height); void resize(int width, int height); diff --git a/engine/layout.h b/engine/layout.h index 3ab713d..8cf19cf 100644 --- a/engine/layout.h +++ b/engine/layout.h @@ -1859,8 +1859,10 @@ class NodeCanvas : public Node glm::vec2 m_dragR_start; glm::vec2 m_pan_start; glm::vec2 m_pan; + glm::vec2 m_cur; float m_zoom_canvas = 1.f; float m_zoom_start; + bool method = true; public: std::unique_ptr m_canvas; ui::Brush m_brush; @@ -1891,15 +1893,24 @@ public: glViewport(c.x, c.y, c.z, c.w); glm::vec2 sz = { m_canvas->m_width, m_canvas->m_height }; - m_canvas->m_mvp = glm::ortho(0.f, box.z, 0.f, box.w, -1.f, 1.f) * - glm::translate(glm::vec3(m_pan + m_size * 0.5f * zoom, 0)) * // pan - glm::scale(glm::vec3(zoom * m_zoom_canvas, zoom * m_zoom_canvas, 1)) * - glm::translate(glm::vec3(-sz/2.f, 0)); - auto plane_mvp = glm::ortho(0.f, box.z, 0.f, box.w, -1.f, 1.f) * - glm::translate(glm::vec3(m_pan + m_size * 0.5f * zoom, 0)) * // pan + m_canvas->m_cam_rot = m_pan * 0.001f; + + //glm::mat4 proj = glm::ortho(0.f, box.z, 0.f, box.w, -1000.f, 1000.f); + glm::mat4 proj = glm::perspective(glm::radians(m_canvas->m_cam_fov), box.z / box.w, 0.1f, 1000.f); + glm::mat4 camera = glm::eulerAngleXY(m_canvas->m_cam_rot.y, m_canvas->m_cam_rot.x); + glm::mat4 transform = + glm::translate(glm::vec3(m_pan + m_size * 0.5f * zoom, -1)) * // pan glm::scale(glm::vec3(zoom * m_zoom_canvas, zoom * m_zoom_canvas, 1)) * - glm::scale(glm::vec3(sz, 1)); + glm::eulerAngleY(glm::radians(0.f)); + + m_canvas->m_mv = camera; + m_canvas->m_proj = proj; + m_canvas->m_box = box; + +// auto plane_mvp = proj * camera * transform * +// glm::scale(glm::vec3(sz, 1)); + auto plane_mvp = proj * camera * glm::translate(glm::vec3(0, 0, -1)); m_sampler.bind(0); ui::ShaderManager::use(kShader::TextureAlpha); @@ -1927,6 +1938,62 @@ public: m_canvas->m_tmp.unbindTexture(); } } + + ui::ShaderManager::use(kShader::Color); + ui::ShaderManager::u_vec4(kShaderUniform::Col, { 1, 0, 0, 1 }); +// auto loc = m_cur; +// auto clip_space = glm::vec2(loc.x, box.w - loc.y - 1.f) / box.zw() * 2.f - 1.f; +// auto inv = glm::inverse(proj * camera); +// auto wp = inv * glm::vec4(clip_space, 0.9, 1); +// wp = wp / wp.w; + + auto unproject = [](glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj, glm::vec3& out_origin, glm::vec3& out_dir) { + auto clip_space = glm::vec2(loc.x, vp.w - loc.y - 1.f) / vp.zw() * 2.f - 1.f; + auto inv = glm::inverse(proj * camera); + auto wp0 = inv * glm::vec4(clip_space, 0, 1); + auto wp1 = inv * glm::vec4(clip_space, .5, 1); + out_origin = (wp0 / wp0.w).xyz(); + out_dir = glm::normalize((wp1 / wp1.w).xyz() - out_origin); + }; + auto intersect = [](glm::vec3 ray_origin, glm::vec3 ray_dir, glm::vec3 plane_origin, glm::vec3 plane_normal, glm::vec3& out_hit) { + float den = glm::dot(ray_dir, plane_normal); + if (den == 0) + return false; // no intersection + float num = glm::dot(plane_origin - ray_origin, plane_normal); + float t = num / den; + if (t > 0) + out_hit = ray_origin + ray_dir * t; + else + // negative intersection + return false; + return true; + }; + glm::vec3 ray_origin, ray_dir; + unproject(m_cur, { 0, 0, box.zw }, camera, proj, ray_origin, ray_dir); + glm::vec3 plane_origin{ 0, 0, -1 }, plane_dir{ 0, 0, 1 }; + glm::vec3 hit; + if (intersect(ray_origin, ray_dir, plane_origin, plane_dir, hit)) + { + glm::mat4 plane_camera = glm::lookAt(plane_origin, plane_dir, { 0, 1, 0 }); + glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); + if (glm::abs(plane_local.x) < 0.5f && glm::abs(plane_local.y) < 0.5f) + { + LOG("hit %f %f %f", plane_local.x, plane_local.y, plane_local.z); + } + } + float fovy = glm::radians(m_canvas->m_cam_fov); + float fovx = fovy * box.z / box.w; + glm::vec2 fov = { fovx, fovy }; + auto fov_t = glm::vec2(m_cur.x, box.w - m_cur.y - 1.f) / box.zw(); + glm::vec2 fov_rot = glm::lerp(-fov*.5f, fov*.5f, fov_t); + glm::mat4 fov_rot_mat = glm::eulerAngleXY(-fov_rot.y, fov_rot.x) * camera; + if (method) + ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera * glm::translate(hit) * glm::scale(glm::vec3(.1))); + else + ui::ShaderManager::u_mat4(kShaderUniform::MVP, proj * camera * glm::translate(hit) * glm::scale(glm::vec3(.1)) * glm::transpose(fov_rot_mat)); + + //NodeBorder::m_plane.draw_fill(); + blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND); m_sampler.unbind(); @@ -1947,16 +2014,16 @@ public: MouseEvent* me = static_cast(e); KeyEvent* ke = static_cast(e); GestureEvent* ge = static_cast(e); - auto loc = me->m_pos - m_pos; + auto loc = (me->m_pos - m_pos) * root()->m_zoom; auto clip_space = glm::vec2(loc.x, m_size.y - loc.y - 1.f) / m_size * 2.f - 1.f; - auto fb_space = glm::inverse(m_canvas->m_mvp) * glm::vec4(clip_space, 0, 1); - auto cur = fb_space.xy(); + //auto fb_space = glm::inverse(m_canvas->m_mvp) * glm::vec4(clip_space, 0, 1); + auto cur = glm::vec2(loc.x, m_size.y - loc.y - 1.f);// fb_space.xy(); switch (e->m_type) { case kEventType::MouseDownL: { - m_canvas->stroke_start(cur, 1.f, m_brush); + m_canvas->stroke_start(loc, 1.f, m_brush); m_dragging = true; mouse_capture(); break; @@ -1978,9 +2045,10 @@ public: break; case kEventType::MouseMove: if (m_dragging) - m_canvas->stroke_update(cur, 1.f); + m_canvas->stroke_update(loc, 1.f); if (m_draggingR) - m_pan = m_pan_start + (me->m_pos - m_dragR_start) * glm::vec2(1, -1); + m_pan = m_pan_start + (me->m_pos - m_dragR_start) * glm::vec2(-1, -1); + m_cur = loc; break; case kEventType::MouseScroll: m_zoom_canvas += me->m_scroll_delta * 0.1f; @@ -1991,7 +2059,10 @@ public: break; case kEventType::KeyDown: if (ke->m_key == kKey::KeyE) + { m_canvas->m_erase = true; + method = !method; + } if (ke->m_key == kKey::AndroidVolumeUp) m_zoom_canvas *= 0.9f; if (ke->m_key == kKey::AndroidVolumeDown) @@ -2006,7 +2077,8 @@ public: m_zoom_start = m_zoom_canvas; break; case kEventType::GestureMove: - m_pan = m_pan_start + ge->m_pos_delta * glm::vec2(1, -1); + m_pan = m_pan_start + ge->m_pos_delta * glm::vec2(-1, -1); + //m_canvas->m_cam_fov = m_zoom_start + ge->m_distance_delta * .001f; m_zoom_canvas = m_zoom_start + ge->m_distance_delta * .001f; break; default: