#include "pch.h" #include "canvas_modes.h" #include "app.h" #include "canvas.h" #include "legacy_canvas_mode_helpers.h" #include "legacy_canvas_draw_merge_services.h" #include "legacy_canvas_stroke_composite_services.h" #include "legacy_ui_overlay_services.h" #include "renderer_gl/opengl_capabilities.h" using pp::legacy_canvas_mode::apply_canvas_mode_capability; using pp::legacy_canvas_mode::apply_canvas_mode_viewport; using pp::legacy_canvas_mode::query_canvas_mode_capability; using pp::legacy_canvas_mode::set_canvas_mode_active_texture_unit; void CanvasModeTransform::init() { m_sphere.create(1.f, glm::radians(-10.f), glm::radians(10.f), glm::radians(-10.f), glm::radians(10.f), 1.f); m_circle.create<16>(1.f); for (int i = 0; i < 6; i++) { m_shape[i].create(); m_points_face[i].clear(); } m_xform = glm::mat4(1); m_xform_local = glm::mat4(1); } void CanvasModeTransform::enter(kCanvasMode prev) { m_commit_on_leave = false; for (int i = 0; i < 6; i++) { m_shape[i].clear(); m_points_face[i].clear(); } if (m_action == ActionType::Import) { float aspect = 1.f; if (m_source_image.data()) { m_tex[0].create(m_source_image); aspect = (float)m_source_image.width / (float)m_source_image.height; } auto center = zw(Canvas::I->m_box) * 0.5f; glm::vec2 bb_sz = glm::vec2(aspect, 1.f) * 100.f * App::I->zoom; glm::vec2 bb_min = center - bb_sz * 0.5f; glm::vec2 bb_max = center + bb_sz * 0.5f; glm::vec2 midpoint = (bb_min + bb_max) * 0.5f; auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1); auto center_mat = glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up)); m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up))); m_xform_local = glm::mat4(1); corners.clear(); corners.emplace_back(bb_min, 0); // A corners.emplace_back(bb_max, 0); // C corners.emplace_back(bb_max.x, bb_min.y, 0); // B corners.emplace_back(bb_min.x, bb_max.y, 0); // D corners.emplace_back(midpoint, 0); corners.emplace_back(midpoint + (bb_max - bb_min) * glm::vec2(0.75f, 0), 0); for (auto& c : corners) c = center_mat * glm::vec4(Canvas::I->point_trace(c), 1); m_points_face[0] = std::vector({ vertex_t(corners[0], { 0, 0 }), vertex_t(corners[2], { 1, 0 }), vertex_t(corners[1], { 1, 1 }), vertex_t(corners[3], { 0, 1 }), }); auto shape3d = triangulate(m_points_face[0]); m_shape[0].update_vertices(shape3d.data(), (int)shape3d.size()); m_commit_on_leave = true; return; } // avoid recursive loop, store the last different mode not using Transform static kCanvasMode last_prev = kCanvasMode::Draw; if (prev != kCanvasMode::Copy && prev != kCanvasMode::Cut && prev != kCanvasMode::Import) last_prev = prev; if (prev != kCanvasMode::MaskFree && prev != kCanvasMode::MaskLine) { Canvas::set_mode(last_prev); return; } auto m = static_cast(Canvas::I->modes[(int)prev][0]); if (m->m_points2d.size() < 3) { Canvas::set_mode(last_prev); return; } Canvas::I->m_smask_active = false; auto points = m->m_points2d; Canvas::I->push_camera(); Canvas::I->set_camera(m->m_selection_cam); glm::vec2 bb_min(FLT_MAX); glm::vec2 bb_max(-FLT_MAX); for (auto p2d : points) { bb_min = glm::min(bb_min, p2d); bb_max = glm::max(bb_max, p2d); } glm::vec2 midpoint = (bb_min + bb_max) * 0.5f; auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1); auto center_mat = glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up)); m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(midpoint), xyz(cam_up))); m_xform_local = glm::mat4(1); corners.clear(); corners.emplace_back(bb_min, 0); corners.emplace_back(bb_max, 0); corners.emplace_back(bb_max.x, bb_min.y, 0); corners.emplace_back(bb_min.x, bb_max.y, 0); corners.emplace_back(midpoint, 0); corners.emplace_back(midpoint + (bb_max-bb_min) * glm::vec2(0.75f, 0), 0); for (auto& c : corners) c = center_mat * glm::vec4(Canvas::I->point_trace(c), 1); for (int plane = 0; plane < 6; plane++) { auto face = Canvas::I->face_to_shape2D(plane); auto shape2d = poly_intersect(points, face); if (shape2d.size() < 3 || face.empty()) { m_shape[plane].clear(); m_points_face[plane].clear(); continue; } m_points_face[plane].reserve(shape2d.size()); glm::vec2 bb_min(Canvas::I->m_size); glm::vec2 bb_max(0, 0); for (auto p2d : shape2d) { p2d.y = Canvas::I->m_box.w - p2d.y - 1; auto p2d_clip = ((p2d / zw(Canvas::I->m_box)) * 2.f - 1.f); auto p3d_plane = Canvas::I->m_plane_unproject[plane] * glm::vec4(p2d_clip, 0, 1); if (p3d_plane.w < 0) continue; auto p3d_norm = -p3d_plane / p3d_plane.z; auto p2d_plane = xy(p3d_norm); auto p2d_plane_raster = (p2d_plane * 0.5f + 0.5f) * Canvas::I->m_size; auto p3d_world = Canvas::I->m_plane_transform[plane] * glm::vec4(p2d_plane, -1, 1); bb_min = glm::min(bb_min, p2d_plane_raster); bb_max = glm::max(bb_max, p2d_plane_raster); //glm::vec3 pt_o, pt_d; //Canvas::I->point_unproject(p2d, pt_o, pt_d); vertex_t v; v.pos = glm::vec4(xyz(p3d_world), 1); v.uvs = p2d_plane_raster; m_points_face[plane].push_back(v); } if (m_points_face[plane].size() < 3) { m_shape[plane].clear(); m_points_face[plane].clear(); continue; } auto bb_sz = bb_max - bb_min; for (auto& v : m_points_face[plane]) { v.uvs2 = v.uvs / Canvas::I->m_size; v.uvs = (v.uvs - bb_min) / bb_sz; v.pos = center_mat * v.pos; } auto shape3d = triangulate(m_points_face[plane]); App::I->render_task([&] { m_shape[plane].update_vertices(shape3d.data(), (int)shape3d.size()); Canvas::I->m_layers[Canvas::I->m_current_layer_idx]->rtt(plane).bindFramebuffer(); m_tex[plane].create(bb_sz.x, bb_sz.y); m_tex[plane].bind(); copy_framebuffer_to_texture_2d( 0, 0, static_cast(bb_min.x), static_cast(bb_min.y), static_cast(bb_sz.x), static_cast(bb_sz.y)); m_tex[plane].unbind(); Canvas::I->m_layers[Canvas::I->m_current_layer_idx]->rtt(plane).unbindFramebuffer(); }); m_commit_on_leave = true; } if (m_action == ActionType::Cut) { auto& layer = Canvas::I->m_layers[Canvas::I->m_current_layer_idx]; glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); auto action = new ActionStroke; action->was_saved = !Canvas::I->m_unsaved; for (int i = 0; i < 6; i++) { auto plane_camera = glm::lookAt(glm::vec3(0), Canvas::I->m_plane_origin[i], Canvas::I->m_plane_tangent[i]); auto mvp = proj * plane_camera * m_xform * m_xform_local; glm::vec2 bb_min(Canvas::I->m_size); glm::vec2 bb_max(0, 0); for (int j = 0; j < 6; j++) { for (auto p : m_points_face[j]) { auto p_clip = mvp * p.pos; auto p_norm = p_clip / p_clip.w; if (p_clip.w < 0 || glm::any(glm::greaterThan(glm::abs(xy(p_norm)), { 1, 1 }))) continue; auto p_raster = (xy(p_norm) * 0.5f + 0.5f) * Canvas::I->m_size; bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster)); bb_max = glm::min(Canvas::I->m_size, glm::max(bb_max, p_raster)); } } glm::vec2 pad(2); bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad); bb_max = glm::min(Canvas::I->m_size, glm::ceil(bb_max) + pad); auto bb_sz = bb_max - bb_min; if (bb_sz.x <= 0.f || bb_sz.y <= 0.f) continue; action->m_image[i] = std::make_unique(bb_sz.x * bb_sz.y * 4); action->m_box[i] = { bb_min, bb_max }; action->m_old_box[i] = layer->box(i); action->m_old_dirty[i] = layer->face(i); layer->face(i) = true; layer->box(i) = { glm::min(xy(layer->box(i)), bb_min), glm::max(zw(layer->box(i)), bb_max), }; App::I->render_task([&] { apply_canvas_mode_viewport(0, 0, layer->w, layer->h); apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false); apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false); set_canvas_mode_active_texture_unit(0); pp::panopainter::setup_legacy_vr_color_shader({ .color = { 0, 0, 0, 0 }, .mvp = mvp, }); layer->rtt(i).bindFramebuffer(); // copy framebuffer to action data layer->rtt(i).readPixelsRgba8( static_cast(bb_min.x), static_cast(bb_min.y), static_cast(bb_sz.x), static_cast(bb_sz.y), action->m_image[i].get()); for (int j = 0; j < 6; j++) m_shape[j].draw_fill(); layer->rtt(i).unbindFramebuffer(); }); } action->m_layer_idx = Canvas::I->m_current_layer_idx; action->m_frame_idx = Canvas::I->layer().m_frame_index; action->m_canvas = Canvas::I; //action->m_stroke = std::move(m_current_stroke); ActionManager::add(action); m_source_image.destroy(); } Canvas::I->pop_camera(); } void CanvasModeTransform::leave(kCanvasMode next) { if (!m_commit_on_leave) return; auto& layer = Canvas::I->m_layers[Canvas::I->m_current_layer_idx]; glm::mat4 proj = glm::perspective(glm::radians(90.f), 1.f, .01f, 1000.f); auto action = new ActionStroke; action->was_saved = !Canvas::I->m_unsaved; for (int i = 0; i < 6; i++) { auto plane_camera = glm::lookAt(glm::vec3(0), Canvas::I->m_plane_origin[i], Canvas::I->m_plane_tangent[i]); auto mv = plane_camera * m_xform * m_xform_local; auto mvp = proj * mv; std::vector poly2d; static std::vector face_corners{ {1,1}, {-1,1}, {-1,-1}, {1,-1} }; for (int j = 0; j < 6; j++) { std::vector poly_cam; poly_cam.reserve(m_points_face[j].size()); for (auto p : m_points_face[j]) poly_cam.push_back(mv * p.pos); auto poly_clipped = poly_clip_near(poly_cam, 0.01f); for (auto p : poly_clipped) { auto p_clip = proj * glm::vec4(p, 1); if (p_clip.w < 0) continue; auto p_norm = p_clip / p_clip.w; poly2d.push_back(p_norm); } } auto clipped = poly_intersect(poly2d, face_corners); glm::vec2 bb_min(Canvas::I->m_size); glm::vec2 bb_max(0, 0); for (auto p_norm : clipped) { auto p_raster = (p_norm * 0.5f + 0.5f) * Canvas::I->m_size; bb_min = glm::max({ 0, 0 }, glm::min(bb_min, p_raster)); bb_max = glm::min(Canvas::I->m_size, glm::max(bb_max, p_raster)); } glm::vec2 pad(2); bb_min = glm::max({ 0, 0 }, glm::floor(bb_min) - pad); bb_max = glm::min(Canvas::I->m_size, glm::ceil(bb_max) + pad); auto bb_sz = bb_max - bb_min; if (clipped.empty() || bb_sz.x <= 0.f || bb_sz.y <= 0.f) continue; action->m_image[i] = std::make_unique(bb_sz.x * bb_sz.y * 4); action->m_box[i] = { bb_min, bb_max }; action->m_old_box[i] = layer->box(i); action->m_old_dirty[i] = layer->face(i); layer->face(i) = true; layer->box(i) = { glm::min(xy(layer->box(i)), bb_min), glm::max(zw(layer->box(i)), bb_max), }; App::I->render_task([&] { layer->rtt(i).bindFramebuffer(); apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false); apply_canvas_mode_capability(pp::renderer::gl::blend_state(), false); set_canvas_mode_active_texture_unit(0); apply_canvas_mode_viewport(0, 0, layer->rtt(i).getWidth(), layer->rtt(i).getHeight()); // save fb content for history layer->rtt(i).readPixelsRgba8( static_cast(bb_min.x), static_cast(bb_min.y), static_cast(bb_sz.x), static_cast(bb_sz.y), action->m_image[i].get()); // copy fb content to texture for blending set_canvas_mode_active_texture_unit(0); Canvas::I->m_tex2[i].bind(); copy_framebuffer_to_texture_2d( static_cast(bb_min.x), static_cast(bb_min.y), static_cast(bb_min.x), static_cast(bb_min.y), static_cast(bb_sz.x), static_cast(bb_sz.y)); // slot for m_tex set_canvas_mode_active_texture_unit(1); for (int j = 0; j < 6; j++) { pp::panopainter::setup_legacy_stroke_composite_shader( pp::panopainter::LegacyStrokeCompositeUniforms { .resolution = Canvas::I->m_size, .mvp = mvp, .texture_slot = 0, .stroke_texture_slot = 1, .layer_alpha = 1.0f, .alpha_lock = false, .mask_enabled = false, .use_fragcoord = true, .blend_mode = 0, .use_dual = false, .use_pattern = false, }); Canvas::I->m_sampler_linear.bind(1); Canvas::I->m_sampler_linear.bind(0); m_tex[j].bind(); m_shape[j].draw_fill(); m_tex[j].unbind(); } layer->rtt(i).unbindFramebuffer(); }); } action->m_layer_idx = Canvas::I->m_current_layer_idx; action->m_canvas = Canvas::I; action->m_frame_idx = Canvas::I->layer().m_frame_index; //action->m_stroke = std::move(m_current_stroke); ActionManager::add(action); layer->optimize(); //auto m = static_cast(Canvas::I->modes[(int)kCanvasMode::MaskFree][0]); //m->clear(); } void CanvasModeTransform::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, const glm::mat4& camera) { const bool depth = query_canvas_mode_capability(pp::renderer::gl::depth_test_state()); apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), false); apply_canvas_mode_capability(pp::renderer::gl::blend_state(), true); for (int i = 0; i < 6; i++) { pp::panopainter::setup_legacy_vr_color_shader({ .color = { 0, 1, 1, .1 }, .mvp = proj * camera * m_xform * m_xform_local, }); m_shape[i].draw_fill(); pp::panopainter::setup_legacy_canvas_draw_merge_texture_shader({ .mvp = proj * camera * m_xform * m_xform_local, .texture_slot = 0, }); set_canvas_mode_active_texture_unit(0); m_tex[i].bind(); Canvas::I->m_sampler_linear.bind(0); m_shape[i].draw_fill(); m_tex[i].unbind(); } auto m2d = Canvas::I->m_proj * Canvas::I->m_mv * m_xform * m_xform_local; for (int i = 0; i < corners.size(); i++) { auto c = m2d * glm::vec4(corners[i], 1); if (c.w < 0) continue; auto c3d = c / c.w; auto c2d = (xy(c3d) * 0.5f + 0.5f) * zw(Canvas::I->m_box); pp::panopainter::setup_legacy_vr_color_shader({ .color = { 1, 1, 1, i == corner_hl ? 1.f : .1f }, .mvp = ortho * glm::translate(glm::vec3(c2d, 0)) * glm::scale(glm::vec3(20.f) * App::I->zoom), }); m_circle.draw_fill(); // draw black border pp::panopainter::setup_legacy_vr_color_shader({ .color = { 0, 0, 0, 1 }, .mvp = ortho * glm::translate(glm::vec3(c2d, 0)) * glm::scale(glm::vec3(20.f) * App::I->zoom), }); m_circle.draw_stroke(); } if (depth) apply_canvas_mode_capability(pp::renderer::gl::depth_test_state(), true); } void CanvasModeTransform::on_MouseEvent(MouseEvent* me, glm::vec2& loc) { auto m2d = glm::scale(glm::vec3(1, -1, 1)) * Canvas::I->m_proj * Canvas::I->m_mv * m_xform * m_xform_local; switch (me->m_type) { case kEventType::MouseDownR: { } break; case kEventType::MouseUpL: m_dragging = false; corner_hl = -1; break; case kEventType::MouseDownL: corner_hl = -1; corners2d.resize(corners.size()); for (int i = 0; i < corners.size(); i++) { auto c = m2d * glm::vec4(corners[i], 1); corners2d[i] = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(Canvas::I->m_box); float d = glm::distance(corners2d[i], loc); if (d < 20.f * App::I->zoom) corner_hl = i; } if (corner_hl != -1) { m_dragging = true; m_drag_start = loc; m_drag_xform = m_xform; m_drag_xform_local = m_xform_local; m_drag_corner = corner_hl; m_drag_corners2d = corners2d; if (m_drag_corner < 4) { m_drag_diag = glm::distance(corners2d[4], corners2d[m_drag_corner]); } } break; case kEventType::MouseMove: { corner_hl = -1; corners2d.resize(corners.size()); for (int i = 0; i < corners.size(); i++) { auto c = m2d * glm::vec4(corners[i], 1); corners2d[i] = ((xy(c) / c.z) * 0.5f + 0.5f) * zw(Canvas::I->m_box); float d = glm::distance(corners2d[i], loc); if (d < 20.f * App::I->zoom) corner_hl = i; } if (m_dragging) { auto cam_up = glm::inverse(Canvas::I->m_mv) * glm::vec4(0, 1, 0, 1); //auto diff = glm::radians(loc - m_drag_start) * 0.1f; //auto m = glm::eulerAngleXY(-diff.y, -diff.x); //m_xform = m * m_drag_xform; if (m_drag_corner > -1 && m_drag_corner < 4) { auto diag = glm::distance(corners2d[4], loc); auto scale = diag / m_drag_diag; m_xform_local = m_drag_xform_local * glm::scale(glm::vec3(scale, scale, 1)); } if (m_drag_corner == 4) { m_xform = glm::inverse(glm::lookAt({ 0, 0, 0 }, Canvas::I->point_trace(loc), xyz(cam_up))); } if (m_drag_corner == 5) { auto a = glm::normalize(m_drag_corners2d[m_drag_corner] - m_drag_corners2d[4]); auto b = glm::normalize(loc - m_drag_corners2d[4]); auto angle = glm::orientedAngle(a, b); m_xform_local = m_drag_xform_local * glm::eulerAngleZ(-angle); } } /* { auto p2d = loc; //p2d.y = Canvas::I->m_box.w - p2d.y - 1; auto p2d_clip = ((p2d / zw(Canvas::I->m_box)) * 2.f - 1.f) * glm::vec2(1, -1); auto p3d_plane = Canvas::I->m_plane_unproject[0] * glm::vec4(p2d_clip, 0, 1); auto p2d_plane = -p3d_plane / p3d_plane.z; // auto p3d_world = Canvas::I->m_plane_transform[0] * glm::vec4(p2d_plane, -1, 1); int x = 0; LOG("pt %f %f %f %f", p2d_plane.x, p2d_plane.y, p2d_plane.z, p2d_plane.w); } */ } break; default: break; } }