From f78f72b607baa1c7685780a97a5d19f3eb661ac1 Mon Sep 17 00:00:00 2001 From: omigamedev Date: Tue, 16 Jun 2026 19:30:55 +0200 Subject: [PATCH] Extract node events, transform mode, and preview pass shells --- cmake/PanoPainterSources.cmake | 3 + docs/modernization/roadmap.md | 22 +- docs/modernization/tasks.md | 15 +- src/canvas_modes.cpp | 551 ----------------- src/legacy_canvas_mode_transform.cpp | 567 ++++++++++++++++++ ...y_node_stroke_preview_execution_services.h | 88 +++ src/legacy_ui_node_event.cpp | 309 ++++++++++ src/legacy_ui_node_event.h | 15 + src/node.cpp | 263 +------- src/node_stroke_preview.cpp | 223 +++---- src/node_stroke_preview.h | 29 - 11 files changed, 1086 insertions(+), 999 deletions(-) create mode 100644 src/legacy_canvas_mode_transform.cpp create mode 100644 src/legacy_ui_node_event.cpp create mode 100644 src/legacy_ui_node_event.h diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index cb628ee2..38cbfa01 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -43,6 +43,8 @@ set(PP_LEGACY_RENDERER_GL_SOURCES set(PP_LEGACY_UI_CORE_SOURCES src/legacy_ui_node_loader.cpp src/legacy_ui_node_loader.h + src/legacy_ui_node_event.cpp + src/legacy_ui_node_event.h src/legacy_ui_node_execution.cpp src/layout.cpp src/node.cpp @@ -70,6 +72,7 @@ set(PP_LEGACY_APP_SOURCES src/canvas_modes.cpp src/legacy_canvas_mode_helpers.cpp src/legacy_canvas_mode_helpers.h + src/legacy_canvas_mode_transform.cpp src/legacy_app_shell_services.cpp src/legacy_app_shell_services.h src/legacy_canvas_tool_services.cpp diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 546b39df..f3c3448d 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -81,11 +81,11 @@ Current hotspot files: - `src/canvas.cpp`: 2122 lines - `src/app_layout.cpp`: 125 lines -- `src/canvas_modes.cpp`: 1626 lines -- `src/node.cpp`: 1368 lines +- `src/canvas_modes.cpp`: 1014 lines +- `src/node.cpp`: 995 lines - `src/main.cpp`: 271 lines - `src/node_panel_brush.cpp`: 652 lines -- `src/node_stroke_preview.cpp`: 910 lines +- `src/node_stroke_preview.cpp`: 751 lines - `src/node_canvas.cpp`: 877 lines - `src/app.cpp`: 575 lines - `src/app_dialogs.cpp`: 168 lines @@ -240,6 +240,10 @@ Current architecture mismatches that must be treated as real blockers: `src/node_stroke_preview.cpp`, while the immediate preview pass-sequencing family inside `draw_stroke_immediate()` now also routes through `NodeStrokePreview::execute_stroke_draw_immediate_pass_sequence(...)`, while + the remaining immediate preview pass shell now also routes through + `execute_legacy_node_stroke_preview_draw_immediate_shell(...)`, which + materially reduces the live preview-pass body even though broader + worker/readback flow still remains inline, while `NodeCanvas::draw()` unmerged-pass blend-gate, layer-orientation, and callback-assembly setup now also route through `execute_node_canvas_draw_unmerged_pass(...)`, which trims another coherent @@ -256,7 +260,11 @@ Current architecture mismatches that must be treated as real blockers: larger stroke commit/sample execution family now also route through `src/legacy_canvas_stroke_commit_services.*` instead of staying inline in `src/canvas.cpp`, which trims another large retained stroke-render and - viewport-state execution family from the live canvas shell, + viewport-state execution family from the live canvas shell, while the + `CanvasModeTransform` interaction family now also routes through + `src/legacy_canvas_mode_transform.cpp` instead of staying inline in + `src/canvas_modes.cpp`, which materially thins another retained canvas-view + and transform-mode execution pocket, while `NodePanelBrush` save/restore/scan/reload/find/get-path ownership now routes through `src/legacy_brush_panel_services.*` instead of staying inline in `src/node_panel_brush.cpp`, which trims another retained brush-workflow @@ -274,7 +282,11 @@ Current architecture mismatches that must be treated as real blockers: `src/canvas_modes.cpp`, while preview stroke preparation, dual-brush setup, and live pass-orchestration request assembly now also route through retained preview execution helpers, - while `Node` child attach/detach/reorder operations now route through named + while `Node::on_event(...)` plus mouse/key capture and release ownership now + also route through `src/legacy_ui_node_event.*` instead of staying inline in + `src/node.cpp`, which materially thins the base scene-graph event shell + without changing its public surface, while `Node` child attach/detach/reorder + operations now route through named local helpers in `src/node.cpp`, and `Node::load_internal(...)` child XML loading now also routes through `src/legacy_ui_node_loader.*`, which makes the scene-graph mutation and child-instantiation paths easier to reason diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 21fa474d..56e98e81 100644 --- a/docs/modernization/tasks.md +++ b/docs/modernization/tasks.md @@ -91,7 +91,7 @@ Status: In Progress Why now: `src/canvas.cpp` is still the biggest single architectural blocker at about -2122 lines. +2122 lines, with `src/canvas_modes.cpp` still large at about 1014 lines. Current slice: - Canvas state-management helpers for picking, clear/clear-all, layer @@ -122,6 +122,10 @@ Current slice: `src/legacy_canvas_mode_helpers.*` instead of staying inline in `src/canvas_modes.cpp`, which trims another coherent retained canvas-view interaction pocket from the broader canvas/render hotspot family. +- The `CanvasModeTransform` interaction family now also lives in + `src/legacy_canvas_mode_transform.cpp` instead of staying inline in + `src/canvas_modes.cpp`, which materially thins another retained + transform-mode pocket from the broader canvas/render hotspot family. Write scope: - `src/canvas.cpp` @@ -182,6 +186,11 @@ Current slice: which trims another coherent setup pocket from `src/node_stroke_preview.cpp` even though worker/readback ownership and broader preview flow still remain inline. +- The remaining immediate preview pass shell in + `NodeStrokePreview::draw_stroke_immediate()` now also routes through + `execute_legacy_node_stroke_preview_draw_immediate_shell(...)`, which + materially reduces the live preview-pass body even though worker/readback + ownership still remains inline. - `NodeCanvas` merged-path per-plane merged-texture draw execution now also routes through `execute_legacy_canvas_draw_merge_layer_texture(...)`. - `NodeCanvas` merged-path and non-blend checkerboard background setup now also @@ -706,6 +715,10 @@ Current slice: now also lives in `src/legacy_ui_node_execution.cpp` instead of staying inline in `src/node.cpp`, which materially thins the base scene-graph file without changing its public surface. +- `Node::on_event(...)` plus mouse/key capture and release now also live in + `src/legacy_ui_node_event.*` instead of staying inline in `src/node.cpp`, + which materially thins the base scene-graph event shell without changing its + public surface. Write scope: - `src/node.cpp` diff --git a/src/canvas_modes.cpp b/src/canvas_modes.cpp index d95057c5..1cda8926 100644 --- a/src/canvas_modes.cpp +++ b/src/canvas_modes.cpp @@ -947,557 +947,6 @@ void CanvasModeFill::on_Draw(const glm::mat4& ortho, const glm::mat4& proj, cons //////////////////////////////////////////////////////////////////// -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; - } -} - //////////////////////////////////////////////////////////////////// void CanvasModeFloodFill::on_MouseEvent(MouseEvent* me, glm::vec2& loc) diff --git a/src/legacy_canvas_mode_transform.cpp b/src/legacy_canvas_mode_transform.cpp new file mode 100644 index 00000000..8eb1a5ef --- /dev/null +++ b/src/legacy_canvas_mode_transform.cpp @@ -0,0 +1,567 @@ +#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; + } +} diff --git a/src/legacy_node_stroke_preview_execution_services.h b/src/legacy_node_stroke_preview_execution_services.h index b595cb11..32b9cf59 100644 --- a/src/legacy_node_stroke_preview_execution_services.h +++ b/src/legacy_node_stroke_preview_execution_services.h @@ -400,6 +400,94 @@ struct LegacyNodeStrokePreviewImmediatePassSequenceRequest { return request.execute_final_composite(); } +[[nodiscard]] inline bool execute_legacy_node_stroke_preview_final_composite( + glm::vec2 size, + glm::vec2 pattern_scale, + const Brush& brush, + const pp::paint_renderer::CanvasStrokeCompositePassPlan& composite_pass, + Texture2D& background_texture, + Texture2D& stroke_texture, + Texture2D& dual_texture, + Texture2D& preview_texture, + Sampler& linear_sampler, + Sampler& repeat_sampler, + std::function bind_pattern_texture, + std::function draw_composite); + +template < + typename Frame, + typename ExecuteDualPass, + typename CaptureBackground, + typename ComputeFrames, + typename BeforeFrame, + typename SetupSampleShader, + typename DrawSample, + typename FinishMainPass, + typename BindPatternTexture, + typename DrawComposite> +[[nodiscard]] inline bool execute_legacy_node_stroke_preview_draw_immediate_shell( + const Brush& brush, + const LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration, + Texture2D& stroke_texture, + RTT& mixer_rtt, + RTT& render_target, + Texture2D& background_texture, + Texture2D& dual_texture, + Texture2D& preview_texture, + Sampler& linear_sampler, + Sampler& repeat_sampler, + bool copy_stroke_destination, + glm::vec2 size, + std::uint32_t stroke_texture_slot, + ExecuteDualPass&& execute_dual_pass, + CaptureBackground&& capture_background, + ComputeFrames&& compute_frames, + BeforeFrame&& before_frame, + SetupSampleShader&& setup_sample_shader, + DrawSample&& draw_sample, + FinishMainPass&& finish_main_pass, + BindPatternTexture&& bind_pattern_texture, + DrawComposite&& draw_composite) +{ + return execute_legacy_node_stroke_preview_immediate_pass_sequence( + LegacyNodeStrokePreviewImmediatePassSequenceRequest { + .execute_dual_pass = std::forward(execute_dual_pass), + .capture_background = std::forward(capture_background), + .execute_main_live_pass = [&]() -> bool { + return execute_legacy_node_stroke_preview_main_live_pass( + make_legacy_node_stroke_preview_main_live_pass_request( + brush, + pass_orchestration, + stroke_texture, + mixer_rtt, + render_target, + copy_stroke_destination, + size, + stroke_texture_slot, + std::forward(compute_frames), + std::forward(before_frame), + std::forward(setup_sample_shader), + std::forward(draw_sample), + std::forward(finish_main_pass))); + }, + .execute_final_composite = [&]() -> bool { + return execute_legacy_node_stroke_preview_final_composite( + size, + glm::vec2(brush.m_pattern_scale), + brush, + pass_orchestration.material.composite_pass, + background_texture, + stroke_texture, + dual_texture, + preview_texture, + linear_sampler, + repeat_sampler, + std::forward(bind_pattern_texture), + std::forward(draw_composite)); + }, + }); +} + [[nodiscard]] inline bool execute_legacy_node_stroke_preview_final_composite( glm::vec2 size, glm::vec2 pattern_scale, diff --git a/src/legacy_ui_node_event.cpp b/src/legacy_ui_node_event.cpp new file mode 100644 index 00000000..447fe116 --- /dev/null +++ b/src/legacy_ui_node_event.cpp @@ -0,0 +1,309 @@ +#include "pch.h" +#include "legacy_ui_node_event.h" + +#include "node.h" +#include "layout.h" + +namespace pp::panopainter { +namespace { + +void transfer_mouse_focus(Node& node, Event* e) +{ + if (e->m_cat != kEventCategory::MouseEvent) + return; + + if (node.child_mouse_focus == node.current_mouse_capture) + return; + + if (!node.is_child(node.current_mouse_capture.get())) + return; + + MouseEvent* me = static_cast(e); + if (node.child_mouse_focus) + { + MouseEvent e2 = *me; + e2.m_type = kEventType::MouseUnfocus; + node.child_mouse_focus->handle_event(&e2); + node.child_mouse_focus->m_mouse_focus = false; + } + MouseEvent e2 = *me; + e2.m_type = kEventType::MouseFocus; + node.current_mouse_capture->handle_event(&e2); + node.child_mouse_focus = node.current_mouse_capture; + node.child_mouse_focus->m_mouse_focus = true; +} + +bool should_skip_children(Node& node, Event* e) +{ + bool skip_children = false; + + if (e->m_cat == kEventCategory::MouseEvent) + { + MouseEvent* me = static_cast(e); + skip_children |= !node.m_mouse_inside && !point_in_rect(me->m_pos, node.m_clip); + } + + skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) && + (node.m_mouse_captured) && (node.root()->current_mouse_capture.get() == &node) && node.m_capture_children; // <-- THIS IS WRONG "!m_capture_children" is correct, but it breaks everything if changed + + return skip_children; +} + +void handle_mouse_focus_transition(Node& node, Event* e, const std::shared_ptr& child) +{ + if (e->m_cat != kEventCategory::MouseEvent || node.child_mouse_focus.get() == child.get()) + return; + + MouseEvent* me = static_cast(e); + if (node.child_mouse_focus && !node.child_mouse_focus->m_destroyed) + { + MouseEvent e2 = *me; + e2.m_type = kEventType::MouseUnfocus; + node.child_mouse_focus->handle_event(&e2); + node.child_mouse_focus->m_mouse_focus = false; + } + MouseEvent e2 = *me; + e2.m_type = kEventType::MouseFocus; + child->handle_event(&e2); + if (!child->m_destroyed) + { + node.child_mouse_focus = child; + node.child_mouse_focus->m_mouse_focus = true; + } +} + +void update_mouse_inside(Node& node, MouseEvent* me) +{ + bool old_inside = node.m_mouse_inside; + node.m_mouse_inside = point_in_rect(me->m_pos, node.m_clip); + if (old_inside == false && node.m_mouse_inside == true) + { + MouseEvent e2 = *me; + e2.m_type = kEventType::MouseEnter; + node.handle_event(&e2); + } + if (old_inside == true && node.m_mouse_inside == false) + { + MouseEvent e2 = *me; + e2.m_type = kEventType::MouseLeave; + node.handle_event(&e2); + } +} + +} // namespace + +kEventResult handle_legacy_ui_node_event(Node& node, Event* e) +{ + kEventResult ret = kEventResult::Available; + + if (e->m_cat == kEventCategory::KeyEvent && node.current_key_capture) + return node.current_key_capture->on_event(e); + + if (node.current_mouse_capture && node.current_mouse_capture.get() != &node) + { + transfer_mouse_focus(node, e); + return node.current_mouse_capture->on_event(e); + } + + // skip mouse events if outside + if (!node.m_display || glm::any(glm::lessThanEqual(zw(node.m_clip), { 0, 0 }))) + return kEventResult::Available; + + if (!should_skip_children(node, e)) + { + // make a copy because any handler can change the children vector + auto children_copy = node.m_children; + for (auto it = children_copy.rbegin(); it != children_copy.rend(); ++it) + { + if ((*it)->on_event(e) == kEventResult::Consumed) + { + if (node.m_flood_events) + { + ret = kEventResult::Consumed; + } + else + { + handle_mouse_focus_transition(node, e, *it); + ret = kEventResult::Consumed; + break; + } + } + } + if (ret == kEventResult::Consumed) + { + if (e->m_cat == kEventCategory::MouseEvent) + update_mouse_inside(node, static_cast(e)); + return kEventResult::Consumed; + } + } + + switch (e->m_cat) + { + case kEventCategory::MouseEvent: + { + if (node.m_mouse_ignore) + break; + MouseEvent* me = static_cast(e); + bool inside = point_in_rect(me->m_pos, node.m_clip); + bool inside_old = node.m_mouse_inside; + node.m_mouse_inside = inside; + switch (e->m_type) + { + case kEventType::MouseScroll: + case kEventType::MouseDownL: + case kEventType::MouseDownR: + case kEventType::MouseUpL: + case kEventType::MouseUpR: + if ((inside || node.m_mouse_captured) && ((node.handle_event(e) == kEventResult::Consumed) || node.m_force_mouse_capture)) + return kEventResult::Consumed; + break; + case kEventType::MouseMove: + if (inside_old == false && inside == true) + { + MouseEvent e2 = *me; + e2.m_type = kEventType::MouseEnter; + node.handle_event(&e2); + } + if (inside || node.m_mouse_captured) + { + ret = node.handle_event(e); + if (node.m_force_mouse_capture) + ret = kEventResult::Consumed; + } + if (inside_old == true && inside == false) + { + MouseEvent e2 = *me; + e2.m_type = kEventType::MouseLeave; + node.handle_event(&e2); + } + break; + default: + if (node.handle_event(e) == kEventResult::Consumed) + return kEventResult::Consumed; + break; + } + break; + } + case kEventCategory::GestureEvent: + { + if (node.m_mouse_ignore) + break; + GestureEvent* ge = static_cast(e); + bool inside = point_in_rect(ge->m_pos, node.m_clip); + node.m_mouse_inside = inside; + if ((inside || node.m_mouse_captured) && node.handle_event(e) == kEventResult::Consumed) + return kEventResult::Consumed; + break; + } + default: + if (node.handle_event(e) == kEventResult::Consumed) + return kEventResult::Consumed; + break; + } + return ret; +} + +void legacy_ui_node_mouse_capture(Node& node) +{ + if (!node.m_parent || !node.m_manager) + return; + + auto root = node.m_manager->get_ref("main"); + if (!root) + return; + + auto& c = root->current_mouse_capture; + auto& s = root->m_capture_stack; + + // already owner of capture + if (c.get() == &node || std::find_if(s.begin(), s.end(), + [&node](const auto& a) { return a.get() == &node; }) != s.end()) + return; + + if (c) + { + if (c->is_child_recursive(&node)) + { + // save on stack + s.push_back(c); + } + else + { + // cancel previous owner + MouseEvent e; + e.m_type = kEventType::MouseCancel; + c->handle_event(&e); + + // TODO: only delete nodes on a different tree, + // so preserve direct parents of this + + // also clear the whole stack + //s.clear(); + + s.push_back(c); + } + } + + // make current + c = node.shared_from_this(); + node.m_mouse_captured = true; +} + +void legacy_ui_node_mouse_release(Node& node) +{ + if (!node.m_parent || !node.m_manager) + return; + + auto root = node.m_manager->get_ref("main"); + if (!root) + return; + + auto& c = root->current_mouse_capture; + auto& s = root->m_capture_stack; + + s.erase(std::remove_if(s.begin(), s.end(), + [&node](const auto& a) { return a.get() == &node; }), s.end()); + if (c.get() == &node) + { + if (s.empty()) + { + c = nullptr; + } + else + { + c = s.back(); + s.pop_back(); + } + } + + node.m_mouse_captured = false; +} + +void legacy_ui_node_key_capture(Node& node) +{ + if (!node.m_parent || !node.m_manager) + return; + + auto root = node.m_manager->get_ref("main"); + if (!root) + return; + + root->current_key_capture = node.shared_from_this(); + node.m_key_captured = true; +} + +void legacy_ui_node_key_release(Node& node) +{ + if (!node.m_parent || !node.m_manager) + return; + + auto root = node.m_manager->get_ref("main"); + if (!root) + return; + + if (root->current_key_capture.get() == &node) + root->current_key_capture = nullptr; + node.m_key_captured = false; +} + +} // namespace pp::panopainter diff --git a/src/legacy_ui_node_event.h b/src/legacy_ui_node_event.h new file mode 100644 index 00000000..5a99a728 --- /dev/null +++ b/src/legacy_ui_node_event.h @@ -0,0 +1,15 @@ +#pragma once + +#include "event.h" + +class Node; + +namespace pp::panopainter { + +kEventResult handle_legacy_ui_node_event(Node& node, Event* e); +void legacy_ui_node_mouse_capture(Node& node); +void legacy_ui_node_mouse_release(Node& node); +void legacy_ui_node_key_capture(Node& node); +void legacy_ui_node_key_release(Node& node); + +} // namespace pp::panopainter diff --git a/src/node.cpp b/src/node.cpp index 424180d2..971f79ea 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "app.h" #include "log.h" +#include "legacy_ui_node_event.h" #include "legacy_ui_node_loader.h" #include "node.h" #include "layout.h" @@ -154,180 +155,7 @@ bool Node::added_to_root() kEventResult Node::on_event(Event* e) { - kEventResult ret = kEventResult::Available; - - if (e->m_cat == kEventCategory::KeyEvent && current_key_capture) - return current_key_capture->on_event(e); - - if (current_mouse_capture && current_mouse_capture.get() != this) - { - if (e->m_cat == kEventCategory::MouseEvent && - child_mouse_focus != current_mouse_capture && - is_child(current_mouse_capture.get())) - { - MouseEvent* me = static_cast(e); - if (child_mouse_focus) - { - MouseEvent e2 = *me; - e2.m_type = kEventType::MouseUnfocus; - child_mouse_focus->handle_event(&e2); - child_mouse_focus->m_mouse_focus = false; - } - MouseEvent e2 = *me; - e2.m_type = kEventType::MouseFocus; - current_mouse_capture->handle_event(&e2); - child_mouse_focus = current_mouse_capture; - child_mouse_focus->m_mouse_focus = true; - } - return current_mouse_capture->on_event(e); - } - - bool skip_children = false; - - // skip mouse events if outside - if (e->m_cat == kEventCategory::MouseEvent) - { - MouseEvent* me = static_cast(e); - skip_children |= !m_mouse_inside && !point_in_rect(me->m_pos, m_clip); - } - - skip_children |= (e->m_cat == kEventCategory::MouseEvent || e->m_cat == kEventCategory::GestureEvent) && - (m_mouse_captured) && (root()->current_mouse_capture.get() == this) && m_capture_children; // <-- THIS IS WRONG "!m_capture_children" is correct, but it breaks everything if changed - - if (!m_display || glm::any(glm::lessThanEqual(zw(m_clip), { 0, 0 }))) - return kEventResult::Available; - - if (!skip_children) - { - // make a copy because any handler can change the children vector - auto children_copy = m_children; - for (auto it = children_copy.rbegin(); it != children_copy.rend(); ++it) - { - if ((*it)->on_event(e) == kEventResult::Consumed) - { - if (m_flood_events) - { - ret = kEventResult::Consumed; - } - else - { - if (e->m_cat == kEventCategory::MouseEvent && child_mouse_focus.get() != it->get()) - { - MouseEvent* me = static_cast(e); - if (child_mouse_focus && !child_mouse_focus->m_destroyed) - { - MouseEvent e2 = *me; - e2.m_type = kEventType::MouseUnfocus; - child_mouse_focus->handle_event(&e2); - child_mouse_focus->m_mouse_focus = false; - } - MouseEvent e2 = *me; - e2.m_type = kEventType::MouseFocus; - (*it)->handle_event(&e2); - if (!(*it)->m_destroyed) - { - child_mouse_focus = *it; - child_mouse_focus->m_mouse_focus = true; - } - //else - //{ - // child_mouse_focus = nullptr; - //} - } - ret = kEventResult::Consumed; - break; - } - } - } - if (ret == kEventResult::Consumed) - { - if (e->m_cat == kEventCategory::MouseEvent) - { - MouseEvent* me = static_cast(e); - bool old_inside = m_mouse_inside; - m_mouse_inside = point_in_rect(me->m_pos, m_clip); - if (old_inside == false && m_mouse_inside == true) - { - MouseEvent e2 = *me; - e2.m_type = kEventType::MouseEnter; - handle_event(&e2); - } - if (old_inside == true && m_mouse_inside == false) - { - MouseEvent e2 = *me; - e2.m_type = kEventType::MouseLeave; - handle_event(&e2); - } - } - return kEventResult::Consumed; - } - } - - switch (e->m_cat) - { - case kEventCategory::MouseEvent: - { - if (m_mouse_ignore) - break; - MouseEvent* me = static_cast(e); - bool inside = point_in_rect(me->m_pos, m_clip); - bool inside_old = m_mouse_inside; - m_mouse_inside = inside; - switch (e->m_type) - { - case kEventType::MouseScroll: - case kEventType::MouseDownL: - case kEventType::MouseDownR: - case kEventType::MouseUpL: - case kEventType::MouseUpR: - if ((inside || m_mouse_captured) && ((handle_event(e) == kEventResult::Consumed) || m_force_mouse_capture)) - return kEventResult::Consumed; - break; - case kEventType::MouseMove: - if (inside_old == false && inside == true) - { - MouseEvent e2 = *me; - e2.m_type = kEventType::MouseEnter; - handle_event(&e2); - } - if (inside || m_mouse_captured) - { - ret = handle_event(e); - if (m_force_mouse_capture) - ret = kEventResult::Consumed; - } - if (inside_old == true && inside == false) - { - MouseEvent e2 = *me; - e2.m_type = kEventType::MouseLeave; - handle_event(&e2); - } - break; - default: - if (handle_event(e) == kEventResult::Consumed) - return kEventResult::Consumed; - break; - } - break; - } - case kEventCategory::GestureEvent: - { - if (m_mouse_ignore) - break; - GestureEvent* ge = static_cast(e); - bool inside = point_in_rect(ge->m_pos, m_clip); - //bool inside_old = m_mouse_inside; - m_mouse_inside = inside; - if ((inside || m_mouse_captured) && handle_event(e) == kEventResult::Consumed) - return kEventResult::Consumed; - break; - } - default: - if (handle_event(e) == kEventResult::Consumed) - return kEventResult::Consumed; - break; - } - return ret; + return pp::panopainter::handle_legacy_ui_node_event(*this, e); } kEventResult Node::handle_event(Event* e) @@ -622,101 +450,22 @@ bool Node::is_child(Node* o) const void Node::mouse_capture() { - if (!m_parent || !m_manager) - return; - - auto root = m_manager->get_ref("main"); - if (!root) return; - - auto& c = root->current_mouse_capture; - auto& s = root->m_capture_stack; - - // already owner of capture - if (c.get() == this || std::find_if(s.begin(), s.end(), - [this](const auto& a) { return a.get() == this; }) != s.end()) - return; - - if (c) - { - if (c->is_child_recursive(this)) - { - // save on stack - s.push_back(c); - } - else - { - // cancel previous owner - MouseEvent e; - e.m_type = kEventType::MouseCancel; - c->handle_event(&e); - - // TODO: only delete nodes on a different tree, - // so preserve direct parents of this - - // also clear the whole stack - //s.clear(); - - s.push_back(c); - } - } - - // make current - c = shared_from_this(); - m_mouse_captured = true; + pp::panopainter::legacy_ui_node_mouse_capture(*this); } void Node::mouse_release() { - if (!m_parent || !m_manager) - return; - - auto root = m_manager->get_ref("main"); - if (!root) return; - - auto& c = root->current_mouse_capture; - auto& s = root->m_capture_stack; - - s.erase(std::remove_if(s.begin(), s.end(), - [this](const auto& a) { return a.get() == this; }), s.end()); - if (c.get() == this) - { - if (s.empty()) - { - c = nullptr; - } - else - { - c = s.back(); - s.pop_back(); - } - } - - m_mouse_captured = false; + pp::panopainter::legacy_ui_node_mouse_release(*this); } void Node::key_capture() { - if (!m_parent || !m_manager) - return; - - auto root = m_manager->get_ref("main"); - if (!root) return; - - root->current_key_capture = shared_from_this(); - m_key_captured = true; + pp::panopainter::legacy_ui_node_key_capture(*this); } void Node::key_release() { - if (!m_parent || !m_manager) - return; - - auto root = m_manager->get_ref("main"); - if (!root) return; - - if (root->current_key_capture.get() == this) - root->current_key_capture = nullptr; - m_key_captured = false; + pp::panopainter::legacy_ui_node_key_release(*this); } Node&& Node::operator=(Node&& o) diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index b8250f9d..90429a4b 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -599,88 +599,79 @@ void NodeStrokePreview::draw_stroke_immediate() ortho_proj)); const bool copy_stroke_destination = pass_orchestration.copy_stroke_destination; pp::panopainter::setup_legacy_stroke_shader(pass_orchestration.stroke_shader); - execute_stroke_draw_immediate_pass_sequence( - prepared_strokes.stroke, - prepared_strokes.dual_stroke, + const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_draw_immediate_shell( *b, - std::move(prepared_strokes.dual_brush), - pass_orchestration, - copy_stroke_destination, - zoom, - size); - - m_rtt.unbindFramebuffer(); - - apply_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height); - apply_stroke_preview_clear_color(cc); -} - -NodeStrokePreview::StrokeMainLivePassRequest -NodeStrokePreview::make_stroke_draw_immediate_main_live_pass_request( - Stroke& stroke, - const Brush& brush, - const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration, - bool copy_stroke_destination, - float zoom, - const glm::vec2& size) -{ - return pp::panopainter::make_legacy_node_stroke_preview_main_live_pass_request( - brush, pass_orchestration, m_tex, m_rtt_mixer, m_rtt, + m_tex_background, + m_tex_dual, + m_tex_preview, + m_sampler_linear, + m_sampler_linear_repeat, copy_stroke_destination, size, pp::panopainter::kLegacyNodeStrokePreviewStrokeTextureSlot, [&] { - return stroke_draw_compute(stroke, zoom); + if (!pass_orchestration.material.dual_pass.enabled) { + return; + } + + pp::panopainter::setup_legacy_stroke_dual_shader(pass_orchestration.material.dual_pass.uses_pattern); + bind_stroke_preview_dual_pass_textures(*prepared_strokes.dual_brush); + pp::panopainter::execute_legacy_stroke_preview_live_pass( + [&] { + m_rtt.clear(); + }, + [&] { + return stroke_draw_compute(prepared_strokes.dual_stroke, zoom); + }, + [](auto& frame) { + frame.col = { 0, 0, 0, 1 }; + }, + [&](auto& frame) { + pp::panopainter::use_legacy_stroke_shader(); + pp::panopainter::apply_legacy_stroke_sample_uniforms( + pp::panopainter::LegacyStrokeSampleUniforms { + .color = frame.col, + .alpha = frame.flow, + .opacity = frame.opacity, + }); + }, + [&](auto& frame) { + /*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex_dual, copy_stroke_destination); + }, + [&] { + pp::panopainter::copy_legacy_node_stroke_preview_framebuffer_to_texture( + m_tex_dual, + size, + pp::panopainter::kLegacyNodeStrokePreviewStrokeTextureSlot); + }); + }, + [&] { + execute_stroke_preview_background_capture_pass( + size, + pass_orchestration.background_colorize, + m_tex_background, + [&] { + m_plane.draw_fill(); + }); + }, + [&] { + return stroke_draw_compute(prepared_strokes.stroke, zoom); }, [&](auto& frame) { - if (brush.m_tip_mix > 0.f) + if (b->m_tip_mix > 0.f) { stroke_draw_mix(xy(frame.m_mixer_rect), zw(frame.m_mixer_rect)); } - frame.col = brush.m_blend_mode != 0 || brush.m_tip_mix > 0.f ? + frame.col = b->m_blend_mode != 0 || b->m_tip_mix > 0.f ? glm::vec4 { .7, .4, .1, 1 } : glm::vec4 { 0, 0, 0, 1 }; frame.flow = glm::max(frame.flow, m_min_flow); }, - [&](auto& frame) { - execute_stroke_draw_immediate_main_live_sample_pass( - brush, - copy_stroke_destination, - frame, - size); - }, - [&](auto& frame) { - /*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex, copy_stroke_destination); - }, - [&] { set_active_texture_unit(stroke_preview_live_slots::kMixer); m_rtt_mixer.unbindTexture(); }); -} - -void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass( - Stroke& dual_stroke, - const Brush& brush, - const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration, - std::shared_ptr dual_brush, - bool copy_stroke_destination, - float zoom, - const glm::vec2& size) -{ - (void)pass_orchestration; - (void)dual_brush; - pp::panopainter::execute_legacy_stroke_preview_live_pass( - [&] { - m_rtt.clear(); - }, - [&] { - return stroke_draw_compute(dual_stroke, zoom); - }, - [](auto& frame) { - frame.col = { 0, 0, 0, 1 }; - }, [&](auto& frame) { pp::panopainter::use_legacy_stroke_shader(); pp::panopainter::apply_legacy_stroke_sample_uniforms( @@ -691,104 +682,24 @@ void NodeStrokePreview::execute_stroke_draw_immediate_dual_pass( }); }, [&](auto& frame) { - /*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex_dual, copy_stroke_destination); + /*auto rect =*/ stroke_draw_samples(frame.shapes, m_tex, copy_stroke_destination); }, [&] { - pp::panopainter::copy_legacy_node_stroke_preview_framebuffer_to_texture( - m_tex_dual, - size, - stroke_preview_composite_slots::kStroke); - }); -} - -void NodeStrokePreview::execute_stroke_draw_immediate_pass_sequence( - Stroke& stroke, - Stroke& dual_stroke, - const Brush& brush, - std::shared_ptr dual_brush, - const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration, - bool copy_stroke_destination, - float zoom, - const glm::vec2& size) -{ - const auto& material = pass_orchestration.material; - const bool sequence_ok = pp::panopainter::execute_legacy_node_stroke_preview_immediate_pass_sequence( - pp::panopainter::LegacyNodeStrokePreviewImmediatePassSequenceRequest { - .execute_dual_pass = [&] { - if (!material.dual_pass.enabled) { - return; - } - - pp::panopainter::setup_legacy_stroke_dual_shader( - material.dual_pass.uses_pattern); - bind_stroke_preview_dual_pass_textures(*dual_brush); - execute_stroke_draw_immediate_dual_pass( - dual_stroke, - brush, - pass_orchestration, - std::move(dual_brush), - copy_stroke_destination, - zoom, - size); - }, - .capture_background = [&] { - execute_stroke_preview_background_capture_pass( - size, - pass_orchestration.background_colorize, - m_tex_background, - [&] { - m_plane.draw_fill(); - }); - }, - .execute_main_live_pass = [&]() -> bool { - return pp::panopainter::execute_legacy_node_stroke_preview_main_live_pass( - make_stroke_draw_immediate_main_live_pass_request( - stroke, - brush, - pass_orchestration, - copy_stroke_destination, - zoom, - size)); - }, - .execute_final_composite = [&]() -> bool { - return pp::panopainter::execute_legacy_node_stroke_preview_final_composite( - size, - glm::vec2(brush.m_pattern_scale), - brush, - material.composite_pass, - m_tex_background, - m_tex, - m_tex_dual, - m_tex_preview, - m_sampler_linear, - m_sampler_linear_repeat, - [&] { - brush.m_pattern_texture ? brush.m_pattern_texture->bind() : unbind_texture_2d(); - }, - [&] { - m_plane.draw_fill(); - }); - }, + set_active_texture_unit(3U); + m_rtt_mixer.unbindTexture(); + }, + [&] { + b->m_pattern_texture ? b->m_pattern_texture->bind() : unbind_texture_2d(); + }, + [&] { + m_plane.draw_fill(); }); assert(sequence_ok); -} -void NodeStrokePreview::execute_stroke_draw_immediate_main_live_sample_pass( - const Brush& brush, - bool copy_stroke_destination, - const StrokeFrame& frame, - const glm::vec2& size) -{ - (void)brush; - (void)copy_stroke_destination; - (void)size; - pp::panopainter::use_legacy_stroke_shader(); - pp::panopainter::apply_legacy_stroke_sample_uniforms( - pp::panopainter::LegacyStrokeSampleUniforms { - .color = frame.col, - .alpha = frame.flow, - .opacity = frame.opacity, - }); + m_rtt.unbindFramebuffer(); + + apply_stroke_preview_viewport(vp.x, vp.y, vp.width, vp.height); + apply_stroke_preview_clear_color(cc); } Image NodeStrokePreview::render_to_image() diff --git a/src/node_stroke_preview.h b/src/node_stroke_preview.h index c4bc3ee4..d7eb46b2 100644 --- a/src/node_stroke_preview.h +++ b/src/node_stroke_preview.h @@ -30,15 +30,6 @@ class NodeStrokePreview : public NodeBorder static Sampler m_sampler_mipmap; static DynamicShape m_brush_shape; Texture2D m_tex_preview; - void execute_stroke_draw_immediate_pass_sequence( - Stroke& stroke, - Stroke& dual_stroke, - const Brush& brush, - std::shared_ptr dual_brush, - const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration, - bool copy_stroke_destination, - float zoom, - const glm::vec2& size); public: using parent = NodeBorder; static std::atomic_int s_instances; @@ -65,26 +56,6 @@ public: // return rect {origin, size} glm::vec4 stroke_draw_samples(std::array& P, Texture2D& blend_tex, bool copy_stroke_destination); std::vector stroke_draw_compute(Stroke& stroke, float zoom) const; - StrokeMainLivePassRequest make_stroke_draw_immediate_main_live_pass_request( - Stroke& stroke, - const Brush& brush, - const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration, - bool copy_stroke_destination, - float zoom, - const glm::vec2& size); - void execute_stroke_draw_immediate_main_live_sample_pass( - const Brush& brush, - bool copy_stroke_destination, - const StrokeFrame& frame, - const glm::vec2& size); - void execute_stroke_draw_immediate_dual_pass( - Stroke& dual_stroke, - const Brush& brush, - const pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationPlan& pass_orchestration, - std::shared_ptr dual_brush, - bool copy_stroke_destination, - float zoom, - const glm::vec2& size); void draw_stroke(); void draw_stroke_immediate(); Image render_to_image();