diff --git a/cmake/PanoPainterSources.cmake b/cmake/PanoPainterSources.cmake index 408a39d9..6765dca5 100644 --- a/cmake/PanoPainterSources.cmake +++ b/cmake/PanoPainterSources.cmake @@ -23,6 +23,8 @@ set(PP_LEGACY_PAINT_DOCUMENT_SOURCES src/canvas_actions.cpp src/canvas_layer.cpp src/legacy_canvas_document_io_services.cpp + src/legacy_canvas_projection_services.cpp + src/legacy_canvas_projection_services.h src/legacy_canvas_state_services.cpp src/event.cpp ) @@ -36,6 +38,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/layout.cpp src/node.cpp src/node_border.cpp diff --git a/docs/modernization/roadmap.md b/docs/modernization/roadmap.md index 380e4672..5975dccb 100644 --- a/docs/modernization/roadmap.md +++ b/docs/modernization/roadmap.md @@ -79,16 +79,16 @@ What is still carrying too much live ownership: Current hotspot files: -- `src/canvas.cpp`: 2645 lines +- `src/canvas.cpp`: 2728 lines - `src/app_layout.cpp`: 125 lines - `src/canvas_modes.cpp`: 1798 lines -- `src/node.cpp`: 1594 lines +- `src/node.cpp`: 1465 lines - `src/main.cpp`: 271 lines -- `src/node_panel_brush.cpp`: 1197 lines -- `src/node_stroke_preview.cpp`: 890 lines +- `src/node_panel_brush.cpp`: 1207 lines +- `src/node_stroke_preview.cpp`: 910 lines - `src/node_canvas.cpp`: 852 lines -- `src/app.cpp`: 502 lines -- `src/app_dialogs.cpp`: 142 lines +- `src/app.cpp`: 575 lines +- `src/app_dialogs.cpp`: 168 lines Current architecture mismatches that must be treated as real blockers: @@ -240,10 +240,17 @@ 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 - `Node` child attach/detach/reorder operations now route through named local - helpers in `src/node.cpp`, which makes the scene-graph mutation paths easier - to reason about without yet reducing the file or moving ownership into - `pp_ui_core`. + 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 + 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 + about without yet reducing the file or moving ownership into `pp_ui_core`, + while `Canvas` point-trace/unproject/project/camera push-pop-get-set and + face-to-shape helpers now also route through + `src/legacy_canvas_projection_services.*` instead of staying inline in + `src/canvas.cpp`. - Modern C++23 usage exists in extracted components, especially `std::span`, explicit result/status objects, and a few concepts, but the live app still does not consistently express ownership, thread affinity, or renderer diff --git a/docs/modernization/tasks.md b/docs/modernization/tasks.md index 8abac284..be1dffa1 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 -2645 lines. +2728 lines. Current slice: - Canvas state-management helpers for picking, clear/clear-all, layer @@ -103,6 +103,11 @@ Current slice: `src/legacy_canvas_document_io_services.cpp` instead of staying inline in `src/canvas.cpp`, which materially reduces document IO ownership in the live render shell. +- Canvas point-trace, unproject, project2D, face-to-shape, and camera + push/pop/get/set helpers now also live in + `src/legacy_canvas_projection_services.*` instead of staying inline in + `src/canvas.cpp`, which trims another coherent non-UI state/query pocket + from the live canvas shell. Write scope: - `src/canvas.cpp` @@ -158,6 +163,11 @@ Current slice: `execute_stroke_draw_immediate_pass_sequence(...)` helper, which removes another live orchestration block from the node even though worker/readback flow still remains in the file. +- `NodeStrokePreview` stroke preparation, dual-brush setup, and live-pass + request assembly now also route through retained preview execution helpers, + which trims another coherent setup pocket from + `src/node_stroke_preview.cpp` even though worker/readback ownership and + broader preview flow still remain 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 @@ -390,7 +400,7 @@ Status: In Progress Why now: `src/app_dialogs.cpp` still mixes document workflow decisions, export routing, -dialog construction, and overlay ownership. +dialog construction, and overlay ownership in one 168-line shell. Current slice: - Informational overlay opener paths for user manual, changelog, about, @@ -439,7 +449,7 @@ Status: In Progress Why now: `src/app.cpp` still carries startup, frame flow, queue draining, recording, -and composition logic in one 502-line file. +and composition logic in one 575-line file. Current slice: - UI observer math now routes through `src/legacy_app_frame_services.cpp` @@ -651,12 +661,19 @@ attached to it. #### ARC-UI-001 - Move Generic Node And Control Code Out Of `pp_legacy_ui_core` -Status: Ready +Status: In Progress Why now: `pp_ui_core` has layout, color, node lifetime, and overlay lifetime, but the generic widget layer still sits in `pp_legacy_ui_core`. +Current slice: +- `Node::load_internal(...)` child XML loading now routes through + `src/legacy_ui_node_loader.*` instead of staying inline in `src/node.cpp`. + That trims another coherent generic node-instantiation pocket and makes the + remaining scene-graph load path easier to isolate, even though ownership has + not yet moved into `pp_ui_core`. + Write scope: - `src/node.cpp` - `src/layout.cpp` diff --git a/src/canvas.cpp b/src/canvas.cpp index de93782d..c04e8013 100644 --- a/src/canvas.cpp +++ b/src/canvas.cpp @@ -10,6 +10,7 @@ #include "legacy_canvas_stroke_edge_services.h" #include "legacy_canvas_stroke_execution_services.h" #include "legacy_canvas_stroke_shader_services.h" +#include "legacy_canvas_projection_services.h" #include "legacy_canvas_stroke_services.h" #include "legacy_ui_gl_dispatch.h" #include "legacy_ui_overlay_services.h" @@ -2028,29 +2029,7 @@ void Canvas::stroke_draw() bool Canvas::point_trace(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id) { - point_unproject(loc, { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir); - glm::vec3 hit; - float hit_t; - for (int i = 0; i < 6; i++) - { - if (ray_intersect(ray_origin, ray_dir, m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i], hit, hit_t)) - { - glm::mat4 plane_camera = glm::lookAt(m_plane_origin[i], m_plane_normal[i], m_plane_tangent[i]); - glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); - if (glm::abs(plane_local.x) < 1.f && glm::abs(plane_local.y) < 1.f) - { - fb_pos.x = -(plane_local.x * 0.5f - 0.5f) * m_width; - fb_pos.y = (plane_local.y * 0.5f + 0.5f) * m_height; - hit_pos = hit; - hit_normal = m_plane_normal[i]; - out_plane_id = i; - return true; - } - else continue; - } - else continue; - } - return false; + return pp::panopainter::legacy_canvas_point_trace(*this, loc, ray_origin, ray_dir, hit_pos, fb_pos, hit_normal, out_plane_id); } /* bool Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& hit_pos, glm::vec2& hit_fb_pos, int plane_id) @@ -2068,52 +2047,20 @@ bool Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& hit_pos, glm::vec2& hit bool Canvas::point_trace_plane(glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id) { - point_unproject(loc, { 0, 0, zw(m_box) }, m_mv, m_proj, ray_origin, ray_dir); - glm::vec3 hit; - float hit_t; - if (ray_intersect(ray_origin, ray_dir, m_plane_origin[plane_id], - m_plane_normal[plane_id], m_plane_tangent[plane_id], hit, hit_t)) - { - glm::mat4 plane_camera = glm::lookAt(m_plane_origin[plane_id], m_plane_normal[plane_id], m_plane_tangent[plane_id]); - glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); - hit_pos = hit; - hit_normal = m_plane_normal[plane_id]; - hit_fb_pos.x = -(plane_local.x * 0.5f - 0.5f); - hit_fb_pos.y = (plane_local.y * 0.5f + 0.5f); - return true; - } - return false; + return pp::panopainter::legacy_canvas_point_trace_plane(*this, loc, ray_origin, ray_dir, hit_pos, hit_normal, hit_fb_pos, plane_id); } void Canvas::point_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) / zw(vp) * 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 = xyz(wp0 / wp0.w); - out_dir = glm::normalize(xyz(wp1 / wp1.w) - out_origin); -}; + pp::panopainter::legacy_canvas_point_unproject(loc, vp, camera, proj, out_origin, out_dir); +} void Canvas::point_unproject(glm::vec2 loc, glm::vec3& out_origin, glm::vec3& out_dir) { - auto clip_space = glm::vec2(loc.x, m_vp.w - loc.y - 1.f) / zw(m_vp) * 2.f - 1.f; - auto inv = glm::inverse(m_proj * m_mv); - auto wp0 = inv * glm::vec4(clip_space, 0, 1); - auto wp1 = inv * glm::vec4(clip_space, .5, 1); - out_origin = xyz(wp0 / wp0.w); - out_dir = glm::normalize(xyz(wp1 / wp1.w) - out_origin); + pp::panopainter::legacy_canvas_point_unproject(*this, loc, out_origin, out_dir); } glm::vec3 Canvas::point_trace(glm::vec2 loc) { - glm::vec3 ray_origin; - glm::vec3 ray_dir; - glm::vec3 hit_pos; - glm::vec3 hit_normal; - glm::vec2 fb_pos; - int plane_id; - if (point_trace(loc, ray_origin, ray_dir, hit_pos, fb_pos, hit_normal, plane_id)) - return hit_pos; - return glm::vec3(0); + return pp::panopainter::legacy_canvas_point_trace(*this, loc); } void Canvas::stroke_commit() { @@ -2736,24 +2683,12 @@ void Canvas::draw_objects(std::function& vertices) { - for (auto& p : vertices) - { - glm::vec3 ro, rd, hit_o, hit_d; - glm::vec2 hit_fb; - int plane_id; - if (point_trace(p.pos, ro, rd, hit_o, hit_fb, hit_d, plane_id)) - p.pos = glm::vec4(hit_o, 1); - } + pp::panopainter::legacy_canvas_project_2d_points(*this, vertices); } glm::vec3 Canvas::project2Dpoint(glm::vec2 pt) { - glm::vec3 ro, rd, hit_o, hit_d; - glm::vec2 hit_fb; - int plane_id; - if (point_trace(pt, ro, rd, hit_o, hit_fb, hit_d, plane_id)) - return glm::vec4(hit_o, 1); - return glm::vec3(0); + return pp::panopainter::legacy_canvas_project_2d_point(*this, pt); } @@ -2762,73 +2697,27 @@ glm::vec3 Canvas::project2Dpoint(glm::vec2 pt) // this can be used for screen space shapes clipping std::vector Canvas::face_to_shape2D(int plane_index) { - static std::array corners{ - glm::vec4(-1.f, +1.f, -1.f, 1.f), // A top-left - glm::vec4(+1.f, +1.f, -1.f, 1.f), // B top-right - glm::vec4(+1.f, -1.f, -1.f, 1.f), // C bottom-right - glm::vec4(-1.f, -1.f, -1.f, 1.f), // D bottom-left - }; - - // compute points in camera space - std::vector pt_cam; - for (auto c : corners) - { - auto pt_world = m_plane_transform[plane_index] * c; - pt_cam.push_back(m_mv * pt_world); - } - - // clip at near plane - pt_cam = poly_clip_near(pt_cam, 0.01); - - // compute windows space - std::vector points; - for (auto p : pt_cam) - { - auto pt_clip = m_proj * glm::vec4(p, 1); - pt_clip = pt_clip / pt_clip.w; - glm::vec2 pt_screen = (glm::vec2(pt_clip) * 0.5f + 0.5f) * zw(m_vp); - pt_screen.y = m_vp.w - pt_screen.y - 1; - points.push_back(pt_screen); - } - return points; + return pp::panopainter::legacy_canvas_face_to_shape_2d(*this, plane_index); } void Canvas::push_camera() { - m_camera_stack.push(get_camera()); + pp::panopainter::legacy_canvas_push_camera(*this); } void Canvas::pop_camera() { - if (!m_camera_stack.empty()) - { - set_camera(m_camera_stack.top()); - m_camera_stack.pop(); - } + pp::panopainter::legacy_canvas_pop_camera(*this); } CameraData Canvas::get_camera() { - CameraData c; - c.m_box = m_box; - c.m_mv = m_mv; - c.m_pan = m_pan; - std::copy_n(m_plane_dir, 6, c.m_plane_dir); - std::copy_n(m_plane_unproject, 6, c.m_plane_unproject); - c.m_proj = m_proj; - c.m_vp = m_vp; - return c; + return pp::panopainter::legacy_canvas_get_camera(*this); } void Canvas::set_camera(const CameraData& c) { - m_box = c.m_box; - m_mv = c.m_mv; - m_pan = c.m_pan; - std::copy_n(c.m_plane_dir, 6, m_plane_dir); - std::copy_n(c.m_plane_unproject, 6, m_plane_unproject); - m_proj = c.m_proj; - m_vp = c.m_vp; + pp::panopainter::legacy_canvas_set_camera(*this, c); } void Canvas::timelapse_reset_encoder() noexcept diff --git a/src/legacy_canvas_projection_services.cpp b/src/legacy_canvas_projection_services.cpp new file mode 100644 index 00000000..ed8587a3 --- /dev/null +++ b/src/legacy_canvas_projection_services.cpp @@ -0,0 +1,179 @@ +#include "pch.h" + +#include "legacy_canvas_projection_services.h" + +#include "canvas.h" + +namespace pp::panopainter { + +bool legacy_canvas_point_trace(Canvas& canvas, glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, + glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id) +{ + legacy_canvas_point_unproject(loc, { 0, 0, zw(canvas.m_box) }, canvas.m_mv, canvas.m_proj, ray_origin, ray_dir); + glm::vec3 hit; + float hit_t; + for (int i = 0; i < 6; i++) + { + if (ray_intersect(ray_origin, ray_dir, canvas.m_plane_origin[i], canvas.m_plane_normal[i], canvas.m_plane_tangent[i], hit, hit_t)) + { + glm::mat4 plane_camera = glm::lookAt(canvas.m_plane_origin[i], canvas.m_plane_normal[i], canvas.m_plane_tangent[i]); + glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); + if (glm::abs(plane_local.x) < 1.f && glm::abs(plane_local.y) < 1.f) + { + fb_pos.x = -(plane_local.x * 0.5f - 0.5f) * canvas.m_width; + fb_pos.y = (plane_local.y * 0.5f + 0.5f) * canvas.m_height; + hit_pos = hit; + hit_normal = canvas.m_plane_normal[i]; + out_plane_id = i; + return true; + } + } + } + return false; +} + +bool legacy_canvas_point_trace_plane(Canvas& canvas, glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, + glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id) +{ + legacy_canvas_point_unproject(loc, { 0, 0, zw(canvas.m_box) }, canvas.m_mv, canvas.m_proj, ray_origin, ray_dir); + glm::vec3 hit; + float hit_t; + if (ray_intersect(ray_origin, ray_dir, canvas.m_plane_origin[plane_id], + canvas.m_plane_normal[plane_id], canvas.m_plane_tangent[plane_id], hit, hit_t)) + { + glm::mat4 plane_camera = glm::lookAt(canvas.m_plane_origin[plane_id], canvas.m_plane_normal[plane_id], canvas.m_plane_tangent[plane_id]); + glm::vec4 plane_local = plane_camera * glm::vec4(hit, 1); + hit_pos = hit; + hit_normal = canvas.m_plane_normal[plane_id]; + hit_fb_pos.x = -(plane_local.x * 0.5f - 0.5f); + hit_fb_pos.y = (plane_local.y * 0.5f + 0.5f); + return true; + } + return false; +} + +void legacy_canvas_point_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) / zw(vp) * 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 = xyz(wp0 / wp0.w); + out_dir = glm::normalize(xyz(wp1 / wp1.w) - out_origin); +} + +void legacy_canvas_point_unproject(Canvas& canvas, glm::vec2 loc, glm::vec3& out_origin, glm::vec3& out_dir) +{ + auto clip_space = glm::vec2(loc.x, canvas.m_vp.w - loc.y - 1.f) / zw(canvas.m_vp) * 2.f - 1.f; + auto inv = glm::inverse(canvas.m_proj * canvas.m_mv); + auto wp0 = inv * glm::vec4(clip_space, 0, 1); + auto wp1 = inv * glm::vec4(clip_space, .5, 1); + out_origin = xyz(wp0 / wp0.w); + out_dir = glm::normalize(xyz(wp1 / wp1.w) - out_origin); +} + +glm::vec3 legacy_canvas_point_trace(Canvas& canvas, glm::vec2 loc) +{ + glm::vec3 ray_origin; + glm::vec3 ray_dir; + glm::vec3 hit_pos; + glm::vec3 hit_normal; + glm::vec2 fb_pos; + int plane_id; + if (legacy_canvas_point_trace(canvas, loc, ray_origin, ray_dir, hit_pos, fb_pos, hit_normal, plane_id)) + return hit_pos; + return glm::vec3(0); +} + +void legacy_canvas_project_2d_points(Canvas& canvas, std::vector& vertices) +{ + for (auto& p : vertices) + { + glm::vec3 ro, rd, hit_o, hit_d; + glm::vec2 hit_fb; + int plane_id; + if (legacy_canvas_point_trace(canvas, p.pos, ro, rd, hit_o, hit_fb, hit_d, plane_id)) + p.pos = glm::vec4(hit_o, 1); + } +} + +glm::vec3 legacy_canvas_project_2d_point(Canvas& canvas, glm::vec2 pt) +{ + glm::vec3 ro, rd, hit_o, hit_d; + glm::vec2 hit_fb; + int plane_id; + if (legacy_canvas_point_trace(canvas, pt, ro, rd, hit_o, hit_fb, hit_d, plane_id)) + return glm::vec4(hit_o, 1); + return glm::vec3(0); +} + +std::vector legacy_canvas_face_to_shape_2d(const Canvas& canvas, int plane_index) +{ + static std::array corners{ + glm::vec4(-1.f, +1.f, -1.f, 1.f), + glm::vec4(+1.f, +1.f, -1.f, 1.f), + glm::vec4(+1.f, -1.f, -1.f, 1.f), + glm::vec4(-1.f, -1.f, -1.f, 1.f), + }; + + std::vector pt_cam; + for (auto c : corners) + { + auto pt_world = canvas.m_plane_transform[plane_index] * c; + pt_cam.push_back(canvas.m_mv * pt_world); + } + + pt_cam = poly_clip_near(pt_cam, 0.01f); + + std::vector points; + for (auto p : pt_cam) + { + auto pt_clip = canvas.m_proj * glm::vec4(p, 1); + pt_clip = pt_clip / pt_clip.w; + glm::vec2 pt_screen = (glm::vec2(pt_clip) * 0.5f + 0.5f) * zw(canvas.m_vp); + pt_screen.y = canvas.m_vp.w - pt_screen.y - 1; + points.push_back(pt_screen); + } + return points; +} + +void legacy_canvas_push_camera(Canvas& canvas) +{ + canvas.m_camera_stack.push(legacy_canvas_get_camera(canvas)); +} + +void legacy_canvas_pop_camera(Canvas& canvas) +{ + if (!canvas.m_camera_stack.empty()) + { + legacy_canvas_set_camera(canvas, canvas.m_camera_stack.top()); + canvas.m_camera_stack.pop(); + } +} + +CameraData legacy_canvas_get_camera(const Canvas& canvas) +{ + CameraData camera; + camera.m_box = canvas.m_box; + camera.m_mv = canvas.m_mv; + camera.m_pan = canvas.m_pan; + std::copy_n(canvas.m_plane_dir, 6, camera.m_plane_dir); + std::copy_n(canvas.m_plane_unproject, 6, camera.m_plane_unproject); + camera.m_proj = canvas.m_proj; + camera.m_vp = canvas.m_vp; + return camera; +} + +void legacy_canvas_set_camera(Canvas& canvas, const CameraData& camera) +{ + canvas.m_box = camera.m_box; + canvas.m_mv = camera.m_mv; + canvas.m_pan = camera.m_pan; + std::copy_n(camera.m_plane_dir, 6, canvas.m_plane_dir); + std::copy_n(camera.m_plane_unproject, 6, canvas.m_plane_unproject); + canvas.m_proj = camera.m_proj; + canvas.m_vp = camera.m_vp; +} + +} // namespace pp::panopainter diff --git a/src/legacy_canvas_projection_services.h b/src/legacy_canvas_projection_services.h new file mode 100644 index 00000000..ba293bcf --- /dev/null +++ b/src/legacy_canvas_projection_services.h @@ -0,0 +1,26 @@ +#pragma once + +#include "canvas_modes.h" +#include "util.h" + +class Canvas; + +namespace pp::panopainter { + +bool legacy_canvas_point_trace(Canvas& canvas, glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, + glm::vec3& hit_pos, glm::vec2& fb_pos, glm::vec3& hit_normal, int& out_plane_id); +bool legacy_canvas_point_trace_plane(Canvas& canvas, glm::vec2 loc, glm::vec3& ray_origin, glm::vec3& ray_dir, + glm::vec3& hit_pos, glm::vec3& hit_normal, glm::vec2& hit_fb_pos, int plane_id); +void legacy_canvas_point_unproject(glm::vec2 loc, glm::vec4 vp, glm::mat4 camera, glm::mat4 proj, + glm::vec3& out_origin, glm::vec3& out_dir); +void legacy_canvas_point_unproject(Canvas& canvas, glm::vec2 loc, glm::vec3& out_origin, glm::vec3& out_dir); +glm::vec3 legacy_canvas_point_trace(Canvas& canvas, glm::vec2 loc); +void legacy_canvas_project_2d_points(Canvas& canvas, std::vector& vertices); +glm::vec3 legacy_canvas_project_2d_point(Canvas& canvas, glm::vec2 pt); +std::vector legacy_canvas_face_to_shape_2d(const Canvas& canvas, int plane_index); +void legacy_canvas_push_camera(Canvas& canvas); +void legacy_canvas_pop_camera(Canvas& canvas); +CameraData legacy_canvas_get_camera(const Canvas& canvas); +void legacy_canvas_set_camera(Canvas& canvas, const CameraData& camera); + +} // namespace pp::panopainter diff --git a/src/legacy_node_stroke_preview_execution_services.h b/src/legacy_node_stroke_preview_execution_services.h index f3a58a86..b595cb11 100644 --- a/src/legacy_node_stroke_preview_execution_services.h +++ b/src/legacy_node_stroke_preview_execution_services.h @@ -9,6 +9,7 @@ #include "legacy_canvas_stroke_services.h" #include "legacy_ui_gl_dispatch.h" #include "paint_renderer/compositor.h" +#include "brush.h" #include "texture.h" #include "util.h" @@ -508,6 +509,38 @@ struct LegacyNodeStrokePreviewPassOrchestrationRequest { glm::mat4 mvp { 1.0f }; }; +[[nodiscard]] inline LegacyNodeStrokePreviewPassOrchestrationRequest +make_legacy_node_stroke_preview_pass_orchestration_request( + pp::renderer::RenderDeviceFeatures features, + glm::vec2 preview_size, + const Brush& brush, + const glm::mat4& mvp) noexcept +{ + return LegacyNodeStrokePreviewPassOrchestrationRequest { + .features = features, + .preview_size = preview_size, + .pattern_scale = brush.m_pattern_scale, + .pattern_flipx = brush.m_pattern_flipx, + .pattern_flipy = brush.m_pattern_flipy, + .pattern_invert = brush.m_pattern_invert, + .pattern_brightness = brush.m_pattern_brightness, + .pattern_contrast = brush.m_pattern_contrast, + .pattern_depth = brush.m_pattern_depth, + .pattern_rand_offset = brush.m_pattern_rand_offset, + .pattern_enabled = brush.m_pattern_enabled, + .pattern_eachsample = brush.m_pattern_eachsample, + .tip_mix = brush.m_tip_mix, + .tip_wet = brush.m_tip_wet, + .tip_noise = brush.m_tip_noise, + .dual_enabled = brush.m_dual_enabled, + .dual_blend_mode = brush.m_dual_blend_mode, + .dual_opacity = brush.m_dual_opacity, + .pattern_blend_mode = brush.m_pattern_blend_mode, + .blend_mode = brush.m_blend_mode, + .mvp = mvp, + }; +} + [[nodiscard]] inline LegacyNodeStrokePreviewPassOrchestrationPlan plan_legacy_node_stroke_preview_pass_orchestration( const LegacyNodeStrokePreviewPassOrchestrationRequest& request) noexcept @@ -595,6 +628,69 @@ struct LegacyNodeStrokePreviewStrokeSetupRequest { int preview_point_count = 100; }; +struct LegacyNodeStrokePreviewPreparedStrokes { + Stroke stroke; + Stroke dual_stroke; + std::shared_ptr dual_brush; +}; + +[[nodiscard]] inline std::shared_ptr make_legacy_node_stroke_preview_dual_brush(const Brush& brush) +{ + auto dual_brush = std::make_shared(); + dual_brush->m_tip_scale = brush.m_dual_scale; + dual_brush->m_tip_angle = brush.m_dual_angle; + dual_brush->m_tip_flow = brush.m_dual_flow; + dual_brush->m_tip_opacity = brush.m_dual_opacity; + dual_brush->m_tip_flipx = brush.m_dual_flipx; + dual_brush->m_tip_flipy = brush.m_dual_flipy; + dual_brush->m_tip_invert = brush.m_dual_invert; + dual_brush->m_blend_mode = brush.m_dual_blend_mode; + dual_brush->m_tip_randflipx = brush.m_dual_randflip; + dual_brush->m_tip_randflipy = brush.m_dual_randflip; + dual_brush->m_tip_size = brush.m_dual_size * brush.m_tip_size; + dual_brush->m_tip_spacing = brush.m_dual_spacing; + dual_brush->m_jitter_scatter = brush.m_dual_scatter; + dual_brush->m_jitter_scatter_bothaxis = brush.m_dual_scatter_bothaxis; + dual_brush->m_jitter_angle = brush.m_dual_rotate; + dual_brush->m_tip_texture = brush.m_dual_texture; + dual_brush->m_tip_aspect = brush.m_dual_aspect; + return dual_brush; +} + +[[nodiscard]] inline LegacyNodeStrokePreviewPreparedStrokes prepare_legacy_node_stroke_preview_strokes( + const std::shared_ptr& brush, + const LegacyNodeStrokePreviewStrokeSetupPlan& stroke_setup, + float camera_fov, + const glm::mat4& camera_rot) +{ + LegacyNodeStrokePreviewPreparedStrokes prepared; + prepared.stroke.m_filter_points = false; + prepared.stroke.m_max_size = stroke_setup.stroke_max_size; + prepared.stroke.m_camera.fov = camera_fov; + prepared.stroke.m_camera.rot = camera_rot; + prepared.stroke.reset(true); + prepared.stroke.start(brush); + + prepared.dual_brush = make_legacy_node_stroke_preview_dual_brush(*brush); + if (stroke_setup.dual_enabled) { + prepared.dual_stroke.m_filter_points = false; + prepared.dual_stroke.m_max_size = stroke_setup.dual_stroke_max_size; + prepared.dual_stroke.m_camera.fov = camera_fov; + prepared.dual_stroke.m_camera.rot = camera_rot; + prepared.dual_stroke.reset(true); + prepared.dual_stroke.start(prepared.dual_brush); + } + + for (const auto& point : stroke_setup.points) { + prepared.stroke.add_point(point.position, point.pressure); + if (stroke_setup.dual_enabled) { + prepared.dual_stroke.add_point(point.position, point.pressure); + } + } + + return prepared; +} + [[nodiscard]] inline glm::vec2 evaluate_legacy_node_stroke_preview_bezier( std::vector control_points, float t) noexcept diff --git a/src/legacy_ui_node_loader.cpp b/src/legacy_ui_node_loader.cpp new file mode 100644 index 00000000..38f87ad3 --- /dev/null +++ b/src/legacy_ui_node_loader.cpp @@ -0,0 +1,180 @@ +#include "pch.h" +#include "legacy_ui_node_loader.h" + +#include "log.h" +#include "node.h" +#include "layout.h" +#include "node_about.h" +#include "node_border.h" +#include "node_button.h" +#include "node_button_custom.h" +#include "node_canvas.h" +#include "node_changelog.h" +#include "node_checkbox.h" +#include "node_color_quad.h" +#include "node_colorwheel.h" +#include "node_combobox.h" +#include "node_dialog_browse.h" +#include "node_dialog_cloud.h" +#include "node_dialog_picker.h" +#include "node_icon.h" +#include "node_image.h" +#include "node_image_texture.h" +#include "node_metadata.h" +#include "node_panel_animation.h" +#include "node_panel_brush.h" +#include "node_panel_color.h" +#include "node_panel_grid.h" +#include "node_panel_layer.h" +#include "node_panel_quick.h" +#include "node_panel_stroke.h" +#include "node_popup_menu.h" +#include "node_scroll.h" +#include "node_slider.h" +#include "node_stroke_preview.h" +#include "node_text.h" +#include "node_text_input.h" +#include "node_tool_bucket.h" +#include "node_usermanual.h" +#include "node_viewport.h" +#include "util.h" + +namespace pp::panopainter { +namespace { + +bool should_skip_child_for_os(const tinyxml2::XMLElement& x_child) +{ + if (auto os = x_child.Attribute("os")) + { + auto osv = split(os, ','); + if (std::find(osv.begin(), osv.end(), PP_OS) == osv.end()) + { + LOG("Element %s not for this os(%s), skipping", x_child.Name(), PP_OS) + return true; + } + } + + return false; +} + +void load_ref_child(Node& parent, const tinyxml2::XMLElement& x_child) +{ + auto ids = x_child.Attribute("id"); + auto id = const_hash(ids); + auto& ref = (*parent.m_manager)[id]->m_children[0]; + auto n = ref->clone(); + n->m_nodeID_s = ids; + n->m_nodeID = id; + parent.add_child(n); +} + +void load_text_child(Node& parent, const tinyxml2::XMLElement& x_child) +{ + auto n = new NodeText(); + parent.add_child(n); + n->load_internal(&x_child, true); + + std::string text; + auto node = x_child.FirstChild(); + while (node) + { + if (auto e = node->ToElement()) + { + if (strcmp(e->Name(), "br") == 0) + text.append("\n"); + } + else if (auto t = node->ToText()) + { + text.append(t->Value()); + } + node = node->NextSibling(); + } + + if (!text.empty()) + n->set_text(text); +} + +Node* instantiate_child_node(std::string_view node_name) +{ + if (node_name == "node") return new Node(); + if (node_name == "border") return new NodeBorder(); + if (node_name == "image") return new NodeImage(); + if (node_name == "image-texture") return new NodeImageTexture(); + if (node_name == "icon") return new NodeIcon(); + if (node_name == "text-input") return new NodeTextInput(); + if (node_name == "button") return new NodeButton(); + if (node_name == "button-custom") return new NodeButtonCustom(); + if (node_name == "combobox") return new NodeComboBox(); + if (node_name == "slider-h") return new NodeSliderH(); + if (node_name == "slider-v") return new NodeSliderV(); + if (node_name == "slider-hue") return new NodeSliderHue(); + if (node_name == "popup-menu") return new NodePopupMenu(); + if (node_name == "viewport") return new NodeViewport(); + if (node_name == "checkbox") return new NodeCheckBox(); + if (node_name == "layer") return new NodeLayer(); + if (node_name == "panel-layer") return new NodePanelLayer(); + if (node_name == "panel-brush") return new NodePanelBrush(); + if (node_name == "panel-color") return new NodePanelColor(); + if (node_name == "panel-stroke") return new NodePanelStroke(); + if (node_name == "panel-grid") return new NodePanelGrid(); + if (node_name == "panel-quick") return new NodePanelQuick(); + if (node_name == "dialog-browse") return new NodeDialogBrowse(); + if (node_name == "dialog-browse-item") return new NodeDialogBrowseItem(); + if (node_name == "dialog-cloud") return new NodeDialogCloud(); + if (node_name == "dialog-cloud-item") return new NodeDialogCloudItem(); + if (node_name == "color-picker") return new NodeColorPicker(); + if (node_name == "about") return new NodeAbout(); + if (node_name == "changelog") return new NodeChangelog(); + if (node_name == "usermanual") return new NodeUserManual(); + if (node_name == "tool-bucket") return new NodeToolBucket(); + if (node_name == "timeline") return new NodeAnimationTimeline(); + if (node_name == "stroke-preview") return new NodeStrokePreview(); + if (node_name == "canvas") return new NodeCanvas(); + if (node_name == "scroll") return new NodeScroll(); + if (node_name == "metadata") return new NodeMetadata(); + if (node_name == "colorwheel") return new NodeColorWheel(); + if (node_name == "color-quad") return new NodeColorQuad(); + return nullptr; +} + +void load_typed_child(Node& parent, const tinyxml2::XMLElement& x_child, std::string_view node_name) +{ + if (auto* n = instantiate_child_node(node_name)) + { + parent.add_child(n); + n->load_internal(&x_child); + return; + } + + LOG("instancing UNKNOWN node: %s", x_child.Name()); + auto* n = new Node(); + parent.add_child(n); + n->load_internal(&x_child); +} + +} // namespace + +void load_legacy_ui_children(Node& parent, const tinyxml2::XMLElement& x_node) +{ + auto x_child = x_node.FirstChildElement(); + while (x_child) + { + if (should_skip_child_for_os(*x_child)) + { + x_child = x_child->NextSiblingElement(); + continue; + } + + std::string_view node_name = x_child->Name(); + if (node_name == "ref") + load_ref_child(parent, *x_child); + else if (node_name == "text") + load_text_child(parent, *x_child); + else + load_typed_child(parent, *x_child, node_name); + + x_child = x_child->NextSiblingElement(); + } +} + +} // namespace pp::panopainter diff --git a/src/legacy_ui_node_loader.h b/src/legacy_ui_node_loader.h new file mode 100644 index 00000000..022b6cc9 --- /dev/null +++ b/src/legacy_ui_node_loader.h @@ -0,0 +1,13 @@ +#pragma once + +namespace tinyxml2 { +class XMLElement; +} + +class Node; + +namespace pp::panopainter { + +void load_legacy_ui_children(Node& parent, const tinyxml2::XMLElement& x_node); + +} // namespace pp::panopainter diff --git a/src/node.cpp b/src/node.cpp index ff805f08..80c90803 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -1,43 +1,11 @@ #include "pch.h" #include "app.h" #include "log.h" +#include "legacy_ui_node_loader.h" #include "node.h" #include "layout.h" #include "util.h" #include "asset.h" -#include "node_border.h" -#include "node_image.h" -#include "node_image_texture.h" -#include "node_icon.h" -#include "node_text.h" -#include "node_text_input.h" -#include "node_button.h" -#include "node_button_custom.h" -#include "node_slider.h" -#include "node_popup_menu.h" -#include "node_viewport.h" -#include "node_checkbox.h" -#include "node_panel_layer.h" -#include "node_panel_brush.h" -#include "node_panel_color.h" -#include "node_panel_stroke.h" -#include "node_color_quad.h" -#include "node_stroke_preview.h" -#include "node_canvas.h" -#include "node_scroll.h" -#include "node_dialog_browse.h" -#include "node_dialog_cloud.h" -#include "node_combobox.h" -#include "node_colorwheel.h" -#include "node_dialog_picker.h" -#include "node_panel_grid.h" -#include "node_about.h" -#include "node_changelog.h" -#include "node_usermanual.h" -#include "node_panel_quick.h" -#include "node_tool_bucket.h" -#include "node_panel_animation.h" -#include "node_metadata.h" namespace { @@ -1430,104 +1398,7 @@ void Node::load_internal(const tinyxml2::XMLElement* x_node, bool skip_children return; } - auto x_child = x_node->FirstChildElement(); - while (x_child) - { - if (auto os = x_child->Attribute("os")) - { - auto osv = split(os, ','); - if (std::find(osv.begin(), osv.end(), PP_OS) == osv.end()) - { - LOG("Element %s not for this os(%s), skipping", x_child->Name(), PP_OS) - x_child = x_child->NextSiblingElement(); - continue; - } - } - - std::string node_name = x_child->Name(); - if (node_name == "ref") - { - auto ids = x_child->Attribute("id"); - auto id = const_hash(ids); - auto& ref = (*m_manager)[id]->m_children[0]; - auto n = ref->clone(); - n->m_nodeID_s = ids; - n->m_nodeID = id; - add_child(n); - } - else if (node_name == "text") - { - auto n = new NodeText(); - add_child(n); - n->load_internal(x_child, true); - std::string text; - auto node = x_child->FirstChild(); - while (node) - { - if (auto e = node->ToElement()) - { - if (strcmp(e->Name(), "br") == 0) - text.append("\n"); - } - else if (auto t = node->ToText()) - { - text.append(t->Value()); - } - node = node->NextSibling(); - } - if (!text.empty()) - n->set_text(text); - } -#define CASE(W,C) else if (node_name == W) { auto n = new C(); add_child(n); n->load_internal(x_child); } - CASE("node", Node) - CASE("border", NodeBorder) - CASE("image", NodeImage) - CASE("image-texture", NodeImageTexture) - CASE("icon", NodeIcon) - CASE("text-input", NodeTextInput) - CASE("button", NodeButton) - CASE("button-custom", NodeButtonCustom) - CASE("combobox", NodeComboBox) - CASE("slider-h", NodeSliderH) - CASE("slider-v", NodeSliderV) - CASE("slider-hue", NodeSliderHue) - CASE("popup-menu", NodePopupMenu) - CASE("viewport", NodeViewport) - CASE("checkbox", NodeCheckBox) - CASE("layer", NodeLayer) - CASE("panel-layer", NodePanelLayer) - CASE("panel-brush", NodePanelBrush) - CASE("panel-color", NodePanelColor) - CASE("panel-stroke", NodePanelStroke) - CASE("panel-grid", NodePanelGrid) - CASE("panel-quick", NodePanelQuick) - CASE("dialog-browse", NodeDialogBrowse) - CASE("dialog-browse-item", NodeDialogBrowseItem) - CASE("dialog-cloud", NodeDialogCloud) - CASE("dialog-cloud-item", NodeDialogCloudItem) - CASE("color-picker", NodeColorPicker) - CASE("about", NodeAbout) - CASE("changelog", NodeChangelog) - CASE("usermanual", NodeUserManual) - CASE("tool-bucket", NodeToolBucket) - CASE("timeline", NodeAnimationTimeline) - CASE("stroke-preview", NodeStrokePreview) - CASE("canvas", NodeCanvas) - CASE("scroll", NodeScroll) - CASE("metadata", NodeMetadata) - CASE("panel-quick", NodePanelQuick) - CASE("colorwheel", NodeColorWheel) - CASE("color-quad", NodeColorQuad) -#undef CASE - else - { - LOG("instancing UNKNOWN node: %s", x_child->Name()); - auto n = new Node(); - add_child(n); - n->load_internal(x_child); - } - x_child = x_child->NextSiblingElement(); - } + pp::panopainter::load_legacy_ui_children(*this, *x_node); loaded(); } diff --git a/src/node_stroke_preview.cpp b/src/node_stroke_preview.cpp index ae639a5b..b8250f9d 100644 --- a/src/node_stroke_preview.cpp +++ b/src/node_stroke_preview.cpp @@ -584,84 +584,26 @@ void NodeStrokePreview::draw_stroke_immediate() const auto& b = m_brush; - Stroke m_stroke; - Stroke m_dual_stroke; - - m_stroke.m_filter_points = false; - m_stroke.m_max_size = stroke_setup.stroke_max_size; - m_stroke.m_camera.fov = Canvas::I->m_cam_fov; - m_stroke.m_camera.rot = Canvas::I->m_cam_rot; - m_stroke.reset(true); - m_stroke.start(b); - - auto dual_brush = std::make_shared(); - dual_brush->m_tip_scale = b->m_dual_scale; - dual_brush->m_tip_angle = b->m_dual_angle; - dual_brush->m_tip_flow = b->m_dual_flow; - dual_brush->m_tip_opacity = b->m_dual_opacity; - dual_brush->m_tip_flipx = b->m_dual_flipx; - dual_brush->m_tip_flipy = b->m_dual_flipy; - dual_brush->m_tip_invert = b->m_dual_invert; - dual_brush->m_blend_mode = b->m_dual_blend_mode; - dual_brush->m_tip_randflipx = b->m_dual_randflip; - dual_brush->m_tip_randflipy = b->m_dual_randflip; - dual_brush->m_tip_size = b->m_dual_size * b->m_tip_size; - dual_brush->m_tip_spacing = b->m_dual_spacing; - dual_brush->m_jitter_scatter = b->m_dual_scatter; - dual_brush->m_jitter_scatter_bothaxis = b->m_dual_scatter_bothaxis; - dual_brush->m_jitter_angle = b->m_dual_rotate; - dual_brush->m_tip_texture = b->m_dual_texture; - dual_brush->m_tip_aspect = b->m_dual_aspect; - - if (stroke_setup.dual_enabled) - { - m_dual_stroke.m_filter_points = false; - m_dual_stroke.m_max_size = stroke_setup.dual_stroke_max_size; - m_dual_stroke.m_camera.fov = Canvas::I->m_cam_fov; - m_dual_stroke.m_camera.rot = Canvas::I->m_cam_rot; - m_dual_stroke.reset(true); - m_dual_stroke.start(dual_brush); - } - - for (const auto& point : stroke_setup.points) - { - m_stroke.add_point(point.position, point.pressure); - if (stroke_setup.dual_enabled) - m_dual_stroke.add_point(point.position, point.pressure); - } + auto prepared_strokes = pp::panopainter::prepare_legacy_node_stroke_preview_strokes( + b, + stroke_setup, + Canvas::I->m_cam_fov, + Canvas::I->m_cam_rot); apply_stroke_preview_capability(pp::renderer::gl::blend_state(), false); const auto pass_orchestration = pp::panopainter::plan_legacy_node_stroke_preview_pass_orchestration( - pp::panopainter::LegacyNodeStrokePreviewPassOrchestrationRequest { - .features = stroke_preview_render_device_features(), - .preview_size = size, - .pattern_scale = b->m_pattern_scale, - .pattern_flipx = b->m_pattern_flipx, - .pattern_flipy = b->m_pattern_flipy, - .pattern_invert = b->m_pattern_invert, - .pattern_brightness = b->m_pattern_brightness, - .pattern_contrast = b->m_pattern_contrast, - .pattern_depth = b->m_pattern_depth, - .pattern_rand_offset = b->m_pattern_rand_offset, - .pattern_enabled = b->m_pattern_enabled, - .pattern_eachsample = b->m_pattern_eachsample, - .tip_mix = b->m_tip_mix, - .tip_wet = b->m_tip_wet, - .tip_noise = b->m_tip_noise, - .dual_enabled = b->m_dual_enabled, - .dual_blend_mode = b->m_dual_blend_mode, - .dual_opacity = b->m_dual_opacity, - .pattern_blend_mode = b->m_pattern_blend_mode, - .blend_mode = b->m_blend_mode, - .mvp = ortho_proj, - }); + pp::panopainter::make_legacy_node_stroke_preview_pass_orchestration_request( + stroke_preview_render_device_features(), + size, + *b, + 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( - m_stroke, - m_dual_stroke, + prepared_strokes.stroke, + prepared_strokes.dual_stroke, *b, - std::move(dual_brush), + std::move(prepared_strokes.dual_brush), pass_orchestration, copy_stroke_destination, zoom,